Created on 2022-07-10.00:00:00 last changed 12 months ago
Proposed resolution:
This wording is relative to n4917.
Modify [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Returns: An object of type C constructed from the elements of r in the following manner:
(1.1) — If convertible_to<range_reference_t<R>, range_value_t<C>> is true:
(1.1.1) — If constructible_from<C, R, Args...> is true:
C(std::forward<R>(r), std::forward<Args>(args)...)(1.1.2) — Otherwise, if constructible_from<C, from_range_t, R, Args...> is true:
C(from_range, std::forward<R>(r), std::forward<Args>(args)...)(1.1.3) — Otherwise, if
(1.1.3.1) — common_range<R> is true,
(1.1.3.2) —
cpp17-input-iteratorif the qualified-id iterator_traits<iterator_t<R>>::iterator_category istruevalid and denotes a type that models derived_from<input_iterator_tag>, and(1.1.3.3) — constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>:
C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(1.1.4) — Otherwise, if
(1.1.4.1) — constructible_from<C, Args...> is true, and
(1.1.4.2) — container-insertable<C, range_reference_t<R>> is true:
[…](1.2) — Otherwise, if input_range<range_reference_t<R>> is true:
to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(1.3) — Otherwise, the program is ill-formed.
[ 2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Voting → WP. ]
[ 2023-01-11; LWG telecon ]
Set status to Tentatively Ready (poll results F6/A0/N1)
[ 2022-08-27; Hewill Kang reopens and suggests a different resolution ]
This issue points out that the standard misuses cpp17-input-iterator to check whether the iterator meets the requirements of Cpp17InputIterator, and proposes to use iterator_traits<I>::iterator_category to check the iterator's category directly, which may lead to the following potential problems:
First, for the range types that model both common_range and input_range, the expression iterator_traits<I>::iterator_category may not be valid, consider#include <ranges> struct I { using difference_type = int; using value_type = int; int operator*() const; I& operator++(); void operator++(int); bool operator==(const I&) const; bool operator==(std::default_sentinel_t) const; }; int main() { auto r = std::ranges::subrange(I{}, I{}); auto v = r | std::ranges::to<std::vector<int>>(0); }
Although I can serve as its own sentinel, it does not model cpp17-input-iterator since postfix operator++ returns void, which causes iterator_traits<R> to be an empty class, making the expression derived_from<iterator_traits<I>::iterator_category, input_iterator_tag> ill-formed.
Second, for common_iterator, iterator_traits<I>::iterator_category does not guarantee a strictly correct iterator category in the current standard. For example, when the above I::operator* returns a non-copyable object that can be converted to int, this makes common_iterator<I, default_sentinel_t> unable to synthesize a C++17-conforming postfix operator++, however, iterator_traits<common_iterator<I, S>>::iterator_category will still give input_iterator_tag even though it's not even a C++17 iterator. Another example is that for input_iterators with difference type of integer-class type, the difference type of the common_iterator wrapped on it is still of the integer-class type, but the iterator_category obtained by the iterator_traits is input_iterator_tag. The proposed resolution only addresses the first issue since I believe that the problem with common_iterator requires a paper.[ 2022-08-23; Reflector poll ]
Set priority to 2 after reflector poll. Set status to Tentatively Ready after five votes in favour during reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4910.
Modify [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Returns: An object of type C constructed from the elements of r in the following manner:
(1.1) — If convertible_to<range_reference_t<R>, range_value_t<C>> is true:
(1.1.1) — If constructible_from<C, R, Args...> is true:
C(std::forward<R>(r), std::forward<Args>(args)...)(1.1.2) — Otherwise, if constructible_from<C, from_range_t, R, Args...> is true:
C(from_range, std::forward<R>(r), std::forward<Args>(args)...)(1.1.3) — Otherwise, if
(1.1.3.1) — common_range<R> is true,
(1.1.3.2) —
cpp17-input-iteratorderived_from<typename iterator_traits<iterator_t<R>>::iterator_category, input_iterator_tag> is true, and(1.1.3.3) — constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...> is true:
C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(1.1.4) — Otherwise, if
(1.1.4.1) — constructible_from<C, Args...> is true, and
(1.1.4.2) — container-insertable<C, range_reference_t<R>> is true:
[…](1.2) — Otherwise, if input_range<range_reference_t<R>> is true:
to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(1.3) — Otherwise, the program is ill-formed.
ranges::to uses cpp17-input-iterator<iterator_t<R>> to check whether an iterator is a Cpp17InputIterator, which misbehaves if there is a std::iterator_traits specialization for that iterator (e.g. if the iterator is a std::common_iterator).
struct MyContainer {
template<class Iter>
MyContainer(Iter, Iter);
char* begin();
char* end();
};
auto nul_terminated = std::views::take_while([](char ch) { return ch != '\0'; });
auto c = nul_terminated("") | std::views::common | std::ranges::to<MyContainer>(); // error
I believe that ranges::to should instead use derived_from<typename iterator_traits<iterator_t<R>>::iterator_category, input_iterator_tag>, which correctly detects the iterator category of a std::common_iterator.
History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-22 15:47:43 | admin | set | status: wp -> c++23 |
2023-02-13 10:17:57 | admin | set | messages: + msg13353 |
2023-02-13 10:17:57 | admin | set | status: voting -> wp |
2023-02-06 15:33:48 | admin | set | status: ready -> voting |
2023-01-11 18:22:42 | admin | set | messages: + msg13200 |
2023-01-11 18:22:42 | admin | set | status: open -> ready |
2022-09-03 17:49:39 | admin | set | messages: + msg12737 |
2022-09-03 17:49:39 | admin | set | status: ready -> open |
2022-08-24 09:52:53 | admin | set | status: new -> ready |
2022-08-23 15:25:16 | admin | set | messages: + msg12700 |
2022-07-10 18:25:33 | admin | set | messages: + msg12575 |
2022-07-10 00:00:00 | admin | create |