diff --git a/libcxx/include/__iterator/common_iterator.h b/libcxx/include/__iterator/common_iterator.h --- a/libcxx/include/__iterator/common_iterator.h +++ b/libcxx/include/__iterator/common_iterator.h @@ -29,6 +29,11 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) +template +concept __can_use_postfix_proxy = + constructible_from, iter_reference_t<_Iter>> && + move_constructible>; + template _Sent> requires (!same_as<_Iter, _Sent> && copyable<_Iter>) class common_iterator { @@ -54,10 +59,6 @@ : __value(_VSTD::forward>(__x)) {} public: - constexpr static bool __valid_for_iter = - constructible_from, iter_reference_t<_Iter>> && - move_constructible>; - constexpr const iter_value_t<_Iter>& operator*() const noexcept { return __value; } @@ -148,7 +149,7 @@ ++*this; return __tmp; } else if constexpr (requires (_Iter& __i) { { *__i++ } -> __referenceable; } || - !__postfix_proxy::__valid_for_iter) { + !__can_use_postfix_proxy<_Iter>) { return _VSTD::__unchecked_get<_Iter>(__hold_)++; } else { __postfix_proxy __p(**this); @@ -261,7 +262,7 @@ using type = decltype(declval&>().operator->()); }; -template +template struct iterator_traits> { using iterator_concept = _If, forward_iterator_tag, @@ -275,7 +276,6 @@ using reference = iter_reference_t<_Iter>; }; - #endif // !defined(_LIBCPP_HAS_NO_RANGES) _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp @@ -14,9 +14,23 @@ #include +#include +#include "test_iterators.h" #include "test_macros.h" #include "types.h" +template +concept HasIteratorConcept = requires { typename T::iterator_concept; }; + +struct NonVoidOutputIterator { + using value_type = int; + using difference_type = std::ptrdiff_t; + const NonVoidOutputIterator& operator*() const; + NonVoidOutputIterator& operator++(); + NonVoidOutputIterator& operator++(int); + void operator=(int) const; +}; + void test() { { using Iter = simple_iterator; @@ -43,17 +57,30 @@ static_assert(!std::same_as); static_assert(std::same_as); } + // Test with an output_iterator that has a void value_type { - using Iter = non_const_deref_iterator; + using Iter = output_iterator; using CommonIter = std::common_iterator>; using IterTraits = std::iterator_traits; - static_assert(std::same_as); - static_assert(std::same_as); - static_assert(std::same_as); + static_assert(!HasIteratorConcept); + static_assert(std::same_as); + static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); - static_assert(std::same_as); + static_assert(std::same_as); + } + // Test with an output_iterator that has a non-void value_type + { + using CommonIter = std::common_iterator>; + using IterTraits = std::iterator_traits; + + static_assert(!HasIteratorConcept); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); } { using Iter = cpp17_input_iterator; diff --git a/libcxx/test/std/iterators/predef.iterators/iterators.common/plus_plus.pass.cpp b/libcxx/test/std/iterators/predef.iterators/iterators.common/plus_plus.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/iterators.common/plus_plus.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/iterators.common/plus_plus.pass.cpp @@ -21,11 +21,10 @@ struct Incomplete; void test() { - int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; - // Reference: http://eel.is/c++draft/iterators.common#common.iter.nav-5 // Case 2: can-reference { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = simple_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -43,6 +42,7 @@ // Case 2: can-reference { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = value_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -60,6 +60,7 @@ // Case 3: postfix-proxy { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = void_plus_plus_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -77,13 +78,13 @@ // Case 2: where this is not referencable or move constructible { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = value_type_not_move_constructible_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); commonIter1++; - // Note: postfix operator++ returns void. - // assert(*(commonIter1++) == 1); + ASSERT_SAME_TYPE(decltype(commonIter1++), void); assert(*commonIter1 == 2); assert(*(++commonIter1) == 3); assert(*commonIter1 == 3); @@ -97,6 +98,7 @@ // Case 2: can-reference { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = cpp17_input_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -114,6 +116,7 @@ // Case 1: forward_iterator { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = forward_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -131,6 +134,7 @@ // Case 1: forward_iterator { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; auto iter1 = random_access_iterator(buffer); auto commonIter1 = std::common_iterator>(iter1); auto commonSent1 = std::common_iterator>(sentinel_type{buffer + 8}); @@ -145,6 +149,31 @@ } assert(commonIter1 == commonSent1); } + + // Increment a common_iterator: iter_value_t is not always valid for + // output iterators (it isn't for our test output_iterator). This is worth testing + // because it gets tricky when we define operator++(int). + { + int buffer[] = {0, 1, 2, 3, 4}; + using Common = std::common_iterator, sentinel_type>; + auto iter = Common(output_iterator(buffer)); + auto sent = Common(sentinel_type{buffer + 5}); + + *iter++ = 90; + assert(buffer[0] == 90); + + *iter = 91; + assert(buffer[1] == 91); + + *++iter = 92; + assert(buffer[2] == 92); + + iter++; + iter++; + assert(iter != sent); + iter++; + assert(iter == sent); + } } int main(int, char**) { diff --git a/libcxx/test/std/iterators/predef.iterators/iterators.common/types.h b/libcxx/test/std/iterators/predef.iterators/iterators.common/types.h --- a/libcxx/test/std/iterators/predef.iterators/iterators.common/types.h +++ b/libcxx/test/std/iterators/predef.iterators/iterators.common/types.h @@ -157,30 +157,6 @@ } }; -template -class non_const_deref_iterator -{ - It it_; - -public: - typedef std::input_iterator_tag iterator_category; - typedef typename std::iterator_traits::value_type value_type; - typedef typename std::iterator_traits::difference_type difference_type; - typedef It pointer; - typedef typename std::iterator_traits::reference reference; - - constexpr It base() const {return it_;} - - non_const_deref_iterator() = default; - explicit constexpr non_const_deref_iterator(It it) : it_(it) {} - - constexpr reference operator*() {return *it_;} // Note: non-const. - - constexpr non_const_deref_iterator& operator++() {++it_; return *this;} - constexpr non_const_deref_iterator operator++(int) - {non_const_deref_iterator tmp(*this); ++(*this); return tmp;} -}; - template struct sentinel_type { T base;