Skip to content

Commit

Permalink
Implement LWG-4074 compatible-joinable-ranges is underconstrained (#…
Browse files Browse the repository at this point in the history
…4814)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
  • Loading branch information
frederick-vs-ja and StephanTLavavej committed Jul 11, 2024
1 parent a395844 commit de1eb6f
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 9 deletions.
43 changes: 34 additions & 9 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -3049,15 +3049,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 @@ -3091,7 +3116,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 @@ -3502,7 +3527,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 @@ -3519,7 +3544,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

0 comments on commit de1eb6f

Please sign in to comment.