This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4214. Missing Preconditions for take/drop adaptor

Section: 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview] Status: New Submitter: Hewill Kang Opened: 2025-02-15 Last modified: 2025-02-24

Priority: Not Prioritized

View other active issues in [range.take.overview].

View all other issues in [range.take.overview].

View all issues with New status.

Discussion:

The take/drop adaptor does not explicitly require N to be non-negative (although the view class does), which makes it possible for some specialized cases to be well-defined when N is negative, since no Preconditions are violated:

auto e = std::views::empty<int> 
       | std::views::take(-1);   // []
auto i = std::views::iota(1, 5)
       | std::views::drop(-1);   // [0, 1, 2, 3, 4]
auto r = std::views::repeat('a', 2)
       | std::views::drop(-1);   // ['a', 'a', 'a']

This is not the intention, we should ban these cases.

Previous resolution [SUPERSEDED]:

This wording is relative to N5001.

  1. Modify 25.7.10.1 [range.take.overview] as indicated:

    -2- The name views::take denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::take(E, F) is ill-formed. Otherwise, the expression views::take(E, F) is expression-equivalent to:

    1. (2.?) Preconditions: static_cast<D>(F) >= 0 is true.

    2. (2.1) — if T is a specialization of empty_view (25.6.2.2 [range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

  2. Modify 25.7.12.1 [range.drop.overview] as indicated:

    -2- The name views::drop denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::drop(E, F) is ill-formed. Otherwise, the expression views::drop(E, F) is expression-equivalent to:

    1. (2.?) Preconditions: static_cast<D>(F) >= 0 is true.

    2. (2.1) — if T is a specialization of empty_view (25.6.2.2 [range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

[2025-02-24]

Upon reflector discussion many preferred to use instead the new Hardened preconditions: element which have been introduced by the recently voted in P3471R4.

Proposed resolution:

This wording is relative to N5001 plus additions from P3471R4.

  1. Modify 25.7.10.1 [range.take.overview] as indicated:

    -2- The name views::take denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::take(E, F) is ill-formed. Otherwise, the expression views::take(E, F) is expression-equivalent to:

    1. (2.?) Hardened preconditions: static_cast<D>(F) >= 0 is true.

    2. (2.1) — if T is a specialization of empty_view (25.6.2.2 [range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.

  2. Modify 25.7.12.1 [range.drop.overview] as indicated:

    -2- The name views::drop denotes a range adaptor object (25.7.2 [range.adaptor.object]). Let E and F be expressions, let T be remove_cvref_t<decltype((E))>, and let D be range_difference_t<decltype((E))>. If decltype((F)) does not model convertible_to<D>, views::drop(E, F) is ill-formed. Otherwise, the expression views::drop(E, F) is expression-equivalent to:

    1. (2.?) Hardened preconditions: static_cast<D>(F) >= 0 is true.

    2. (2.1) — if T is a specialization of empty_view (25.6.2.2 [range.empty.view]), then ((void)F, decay-copy(E)), except that the evaluations of E and F are indeterminately sequenced.