Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement LWG-3564: transform_view::iterator<true>::value_type and iterator_category should use const F& #2961

Merged
merged 11 commits into from
Aug 5, 2022
24 changes: 13 additions & 11 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -1903,20 +1903,22 @@ namespace ranges {
template <bool _Const>
class _Sentinel;

template <class _Base>
template <bool _Const>
struct _Category_base {};

template <forward_range _Base>
struct _Category_base<_Base> {
using iterator_category =
conditional_t<is_lvalue_reference_v<invoke_result_t<_Fn&, range_reference_t<_Base>>>,
conditional_t<derived_from<_Iter_cat_t<iterator_t<_Base>>, contiguous_iterator_tag>,
random_access_iterator_tag, _Iter_cat_t<iterator_t<_Base>>>,
input_iterator_tag>;
template <bool _Const>
requires forward_range<_Maybe_const<_Const, _Vw>>
struct _Category_base<_Const> {
using _Base = _Maybe_const<_Const, _Vw>;
using iterator_category = conditional_t<
is_lvalue_reference_v<invoke_result_t<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>>>,
conditional_t<derived_from<_Iter_cat_t<iterator_t<_Base>>, contiguous_iterator_tag>,
random_access_iterator_tag, _Iter_cat_t<iterator_t<_Base>>>,
input_iterator_tag>;
};

template <bool _Const>
class _Iterator : public _Category_base<_Maybe_const<_Const, _Vw>> {
class _Iterator : public _Category_base<_Const> {
private:
template <bool>
friend class _Iterator;
Expand Down Expand Up @@ -1946,8 +1948,8 @@ namespace ranges {
using iterator_concept = conditional_t<random_access_range<_Base>, random_access_iterator_tag,
conditional_t<bidirectional_range<_Base>, bidirectional_iterator_tag,
conditional_t<forward_range<_Base>, forward_iterator_tag, input_iterator_tag>>>;
using value_type = remove_cvref_t<invoke_result_t<_Fn&, range_reference_t<_Base>>>;
using difference_type = range_difference_t<_Base>;
using value_type = remove_cvref_t<invoke_result_t<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>>>;
using difference_type = range_difference_t<_Base>;

// clang-format off
_Iterator() requires default_initializable<iterator_t<_Base>> = default;
Expand Down
47 changes: 47 additions & 0 deletions tests/std/tests/P0896R4_views_transform/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,50 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) {
return true;
}

// Test a function object whose const and non-const versions behave differently
struct difference_teller {
constexpr auto& operator()(auto&& x) noexcept {
auto& ref = x;
return ref;
}

constexpr auto operator()(auto&& x) const noexcept {
return type_identity<decltype(x)>{};
}
};

template <ranges::input_range Rng>
strega-nil-ms marked this conversation as resolved.
Show resolved Hide resolved
constexpr void test_difference_on_const_functor(Rng&& rng) {
using ranges::transform_view, ranges::input_range, ranges::forward_range, ranges::bidirectional_range,
ranges::random_access_range, ranges::iterator_t, ranges::range_reference_t, ranges::range_value_t;

using V = views::all_t<Rng>;
using TV = transform_view<V, difference_teller>;

auto r = forward<Rng>(rng) | views::transform(difference_teller{});
STATIC_ASSERT(is_same_v<decltype(r), TV>);

STATIC_ASSERT(is_lvalue_reference_v<range_reference_t<TV>>);
if constexpr (input_range<const TV>) {
STATIC_ASSERT(is_object_v<range_reference_t<const TV>>);
STATIC_ASSERT(!is_same_v<range_value_t<TV>, range_value_t<const TV>>);
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
}

if constexpr (forward_range<V>) {
using It = iterator_t<V>;
using TVIt = iterator_t<TV>;
using VItCat = typename iterator_traits<It>::iterator_category;
using TVItCat = typename iterator_traits<TVIt>::iterator_category;
STATIC_ASSERT(
is_same_v<TVItCat, VItCat> //
|| (is_same_v<TVItCat, random_access_iterator_tag> && is_same_v<VItCat, contiguous_iterator_tag>) );
}

if constexpr (forward_range<const V>) {
STATIC_ASSERT(is_same_v<typename iterator_traits<iterator_t<const TV>>::iterator_category, input_iterator_tag>);
}
}

static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7};
static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15};

Expand All @@ -329,6 +373,9 @@ struct instantiator {
static constexpr void call() {
R r{some_ints};
test_one(r, transformed_ints);

R r2{some_ints};
test_difference_on_const_functor(r2);
}
};

Expand Down