Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -3047,15 +3047,40 @@ namespace ranges {
} // namespace views

#if _HAS_CXX23
template <class _Rng, class _Pat>
concept _Compatible_joinable_ranges =
common_with<range_value_t<_Rng>, range_value_t<_Pat>>
&& common_reference_with<range_reference_t<_Rng>, range_reference_t<_Pat>>
&& common_reference_with<range_rvalue_reference_t<_Rng>, range_rvalue_reference_t<_Pat>>;
template <class... _Rngs>
using _Concat_reference_t = common_reference_t<range_reference_t<_Rngs>...>;

template <class... _Rngs>
using _Concat_value_t = common_type_t<range_value_t<_Rngs>...>;

template <class... _Rngs>
using _Concat_rvalue_reference_t = common_reference_t<range_rvalue_reference_t<_Rngs>...>;

template <class _Ref, class _RRef, class _It>
concept _Concat_indirectly_readable_impl = requires(const _It __i) {
{ *__i } -> convertible_to<_Ref>;
{ _RANGES iter_move(__i) } -> convertible_to<_RRef>;
};

template <class... _Rngs>
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 <class... _Rngs>
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 <input_range _Vw, forward_range _Pat>
requires view<_Vw> && input_range<range_reference_t<_Vw>> && view<_Pat>
&& _Compatible_joinable_ranges<range_reference_t<_Vw>, _Pat>
&& _Concatable<range_reference_t<_Vw>, _Pat>
class join_with_view;

template <class _Vw, class _Pat>
Expand Down Expand Up @@ -3089,7 +3114,7 @@ namespace ranges {

_EXPORT_STD template <input_range _Vw, forward_range _Pat>
requires view<_Vw> && input_range<range_reference_t<_Vw>> && view<_Pat>
&& _Compatible_joinable_ranges<range_reference_t<_Vw>, _Pat>
&& _Concatable<range_reference_t<_Vw>, _Pat>
class join_with_view : public _Join_with_view_outer_iter_base<_Vw, _Pat> {
private:
template <bool _Const>
Expand Down Expand Up @@ -3500,7 +3525,7 @@ namespace ranges {

_NODISCARD constexpr auto begin() const
requires forward_range<const _Vw> && forward_range<const _Pat> && is_reference_v<_InnerRng<true>>
&& input_range<_InnerRng<true>>
&& input_range<_InnerRng<true>> && _Concatable<range_reference_t<const _Vw>, const _Pat>
{
return _Iterator<true>{*this, _RANGES begin(_Range)};
}
Expand All @@ -3517,7 +3542,7 @@ namespace ranges {

_NODISCARD constexpr auto end() const
requires forward_range<const _Vw> && forward_range<const _Pat> && is_reference_v<_InnerRng<true>>
&& input_range<_InnerRng<true>>
&& input_range<_InnerRng<true>> && _Concatable<range_reference_t<const _Vw>, const _Pat>
{
if constexpr (forward_range<_InnerRng<true>> && common_range<_Vw> && common_range<_InnerRng<true>>) {
return _Iterator<true>{*this, _RANGES end(_Range)};
Expand Down
101 changes: 101 additions & 0 deletions tests/std/tests/P2441R2_views_join_with/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <ranges>
#include <span>
#include <string_view>
Expand Down Expand Up @@ -674,6 +675,106 @@ void test_lwg3700() { // COMPILE-ONLY
static_assert(!CanMemberEnd<const J>);
}

// LWG-4074 "compatible-joinable-ranges is underconstrained"

template <bool CanCommonRead>
struct ValCommon;

template <bool CanCommonRead>
struct RefCommon;

template <bool CanCommonRead>
struct ValX {
operator ValCommon<CanCommonRead>() const;
};

template <bool CanCommonRead>
struct RefX {
operator ValX<CanCommonRead>() const;
operator RefCommon<CanCommonRead>() const;
};

template <bool CanCommonRead>
struct IterX {
using value_type = ValX<CanCommonRead>;
using difference_type = ptrdiff_t;

RefX<CanCommonRead> operator*() const;
IterX& operator++();
IterX operator++(int);

friend bool operator==(const IterX&, const IterX&);
};

template <bool CanCommonRead>
struct ValY {
operator ValCommon<CanCommonRead>() const;
};

template <bool CanCommonRead>
struct RefY {
operator ValY<CanCommonRead>() const;
operator RefCommon<CanCommonRead>() const;
};

template <bool CanCommonRead>
struct IterY {
using value_type = ValY<CanCommonRead>;
using difference_type = ptrdiff_t;

RefY<CanCommonRead> operator*() const;
IterY& operator++();
IterY operator++(int);

friend bool operator==(const IterY&, const IterY&);
};

template <bool CanCommonRead>
struct ValCommon {};

template <bool CanCommonRead>
struct std::common_type<ValX<CanCommonRead>, ValY<CanCommonRead>> {
using type = ValCommon<CanCommonRead>;
};
template <bool CanCommonRead>
struct std::common_type<ValY<CanCommonRead>, ValX<CanCommonRead>> {
using type = ValCommon<CanCommonRead>;
};

template <bool CanCommonRead>
struct RefCommon {
operator ValCommon<CanCommonRead>() const
requires CanCommonRead;
};

template <bool CanCommonRead, template <class> class XQual, template <class> class YQual>
requires convertible_to<XQual<RefX<CanCommonRead>>, RefCommon<CanCommonRead>>
&& convertible_to<YQual<RefY<CanCommonRead>>, RefCommon<CanCommonRead>>
struct std::basic_common_reference<RefX<CanCommonRead>, RefY<CanCommonRead>, XQual, YQual> {
using type = RefCommon<CanCommonRead>;
};

template <bool CanCommonRead, template <class> class YQual, template <class> class XQual>
requires convertible_to<YQual<RefY<CanCommonRead>>, RefCommon<CanCommonRead>>
&& convertible_to<XQual<RefX<CanCommonRead>>, RefCommon<CanCommonRead>>
struct std::basic_common_reference<RefY<CanCommonRead>, RefX<CanCommonRead>, YQual, XQual> {
using type = RefCommon<CanCommonRead>;
};

static_assert(!CanViewJoinWith<span<ranges::subrange<IterX<false>>>, ranges::subrange<IterY<false>>>);
static_assert(CanViewJoinWith<span<ranges::subrange<IterX<true>>>, ranges::subrange<IterY<true>>>);

struct NonConstReadableRange {
const ranges::subrange<IterX<true>>* begin();
const ranges::subrange<IterX<true>>* end();

const ranges::subrange<IterX<false>>* begin() const;
const ranges::subrange<IterX<false>>* end() const;
};

static_assert(CanViewJoinWith<NonConstReadableRange&, ranges::subrange<IterY<true>>>);
static_assert(!CanViewJoinWith<const NonConstReadableRange&, ranges::subrange<IterY<false>>>);

int main() {
{
auto filtered_and_joined =
Expand Down