diff --git a/stl/inc/ranges b/stl/inc/ranges index 3f8a7cc4150..6e6f04d9ad0 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3047,15 +3047,40 @@ namespace ranges { } // namespace views #if _HAS_CXX23 - template - concept _Compatible_joinable_ranges = - common_with, range_value_t<_Pat>> - && common_reference_with, range_reference_t<_Pat>> - && common_reference_with, range_rvalue_reference_t<_Pat>>; + template + using _Concat_reference_t = common_reference_t...>; + + template + using _Concat_value_t = common_type_t...>; + + template + using _Concat_rvalue_reference_t = common_reference_t...>; + + template + concept _Concat_indirectly_readable_impl = requires(const _It __i) { + { *__i } -> convertible_to<_Ref>; + { _RANGES iter_move(__i) } -> convertible_to<_RRef>; + }; + + template + concept _Concat_indirectly_readable = + common_reference_with<_Concat_reference_t<_Rngs...>&&, _Concat_value_t<_Rngs...>&> + && common_reference_with<_Concat_reference_t<_Rngs...>&&, _Concat_rvalue_reference_t<_Rngs...>&&> + && common_reference_with<_Concat_rvalue_reference_t<_Rngs...>&&, const _Concat_value_t<_Rngs...>&> + && (_Concat_indirectly_readable_impl<_Concat_reference_t<_Rngs...>, _Concat_rvalue_reference_t<_Rngs...>, + iterator_t<_Rngs>> + && ...); + + template + concept _Concatable = requires { + typename _Concat_reference_t<_Rngs...>; + typename _Concat_value_t<_Rngs...>; + typename _Concat_rvalue_reference_t<_Rngs...>; + } && _Concat_indirectly_readable<_Rngs...>; _EXPORT_STD template requires view<_Vw> && input_range> && view<_Pat> - && _Compatible_joinable_ranges, _Pat> + && _Concatable, _Pat> class join_with_view; template @@ -3089,7 +3114,7 @@ namespace ranges { _EXPORT_STD template requires view<_Vw> && input_range> && view<_Pat> - && _Compatible_joinable_ranges, _Pat> + && _Concatable, _Pat> class join_with_view : public _Join_with_view_outer_iter_base<_Vw, _Pat> { private: template @@ -3500,7 +3525,7 @@ namespace ranges { _NODISCARD constexpr auto begin() const requires forward_range && forward_range && is_reference_v<_InnerRng> - && input_range<_InnerRng> + && input_range<_InnerRng> && _Concatable, const _Pat> { return _Iterator{*this, _RANGES begin(_Range)}; } @@ -3517,7 +3542,7 @@ namespace ranges { _NODISCARD constexpr auto end() const requires forward_range && forward_range && is_reference_v<_InnerRng> - && input_range<_InnerRng> + && input_range<_InnerRng> && _Concatable, const _Pat> { if constexpr (forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { return _Iterator{*this, _RANGES end(_Range)}; diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 0634aa8c1a6..4af69a6c4a8 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -674,6 +675,106 @@ void test_lwg3700() { // COMPILE-ONLY static_assert(!CanMemberEnd); } +// LWG-4074 "compatible-joinable-ranges is underconstrained" + +template +struct ValCommon; + +template +struct RefCommon; + +template +struct ValX { + operator ValCommon() const; +}; + +template +struct RefX { + operator ValX() const; + operator RefCommon() const; +}; + +template +struct IterX { + using value_type = ValX; + using difference_type = ptrdiff_t; + + RefX operator*() const; + IterX& operator++(); + IterX operator++(int); + + friend bool operator==(const IterX&, const IterX&); +}; + +template +struct ValY { + operator ValCommon() const; +}; + +template +struct RefY { + operator ValY() const; + operator RefCommon() const; +}; + +template +struct IterY { + using value_type = ValY; + using difference_type = ptrdiff_t; + + RefY operator*() const; + IterY& operator++(); + IterY operator++(int); + + friend bool operator==(const IterY&, const IterY&); +}; + +template +struct ValCommon {}; + +template +struct std::common_type, ValY> { + using type = ValCommon; +}; +template +struct std::common_type, ValX> { + using type = ValCommon; +}; + +template +struct RefCommon { + operator ValCommon() const + requires CanCommonRead; +}; + +template class XQual, template class YQual> + requires convertible_to>, RefCommon> + && convertible_to>, RefCommon> +struct std::basic_common_reference, RefY, XQual, YQual> { + using type = RefCommon; +}; + +template class YQual, template class XQual> + requires convertible_to>, RefCommon> + && convertible_to>, RefCommon> +struct std::basic_common_reference, RefX, YQual, XQual> { + using type = RefCommon; +}; + +static_assert(!CanViewJoinWith>>, ranges::subrange>>); +static_assert(CanViewJoinWith>>, ranges::subrange>>); + +struct NonConstReadableRange { + const ranges::subrange>* begin(); + const ranges::subrange>* end(); + + const ranges::subrange>* begin() const; + const ranges::subrange>* end() const; +}; + +static_assert(CanViewJoinWith>>); +static_assert(!CanViewJoinWith>>); + int main() { { auto filtered_and_joined =