The iterators for transform views (views::transform, views::zip_transform,
and views::adjacent_transform) now store a __detail::__func_handle instead
of a pointer to the view object (_M_parent).
The behavior of the __func_handle specialization depends on the _FunctorType
template parameter:
* _FunctionPtr: Used if the functor is a function pointer. The pointer is
stored directly in __func_handle and the iterator, avoiding double
indirection through a pointer to the function pointer.
* _MemberPointer: Used for data or function member pointers. This behaves
similarly to _FunctionPtr, but uses __invoke for invocations.
* _StaticOperator: Used if the operator() selected by overload resolution
for the iterator reference is static. In this case, __func_handle is empty,
reducing the iterator size.
* _Stateful: Used for all remaining cases. __func_handle stores a pointer
to the functor object stored within the view. Only for this specialization is
the cv-qualification of the functor template parameter (_Fn) relevant, and
both const and mutable specializations are generated.
As a consequence of these changes, the iterators of transform views no longer
depend on the view object when __func_handle is specialized for values other
than _Stateful. The corresponding views are not marked as borrowed_range,
as they are not marked as such in the standard.
Storing function member pointers directly increases the iterator size in that
specific case, but this is deemed beneficial for consistent treatment of
function and data member pointers.
To avoid materializing temporaries when the underlying iterator(s) return a
prvalue, the _M_call_deref and _M_call_subscript methods of __func_handle are
defined to accept the iterator(s), which are then dereferenced as arguments
of the functor.
Using _Fd::operator()(*__iters...) inside requires expression is only
supported since clang-20, however at the point of GCC-16 release, clang-22
should be already available.
libstdc++-v3/ChangeLog:
* include/std/ranges (__detail::__func_handle)
(__detail::func_handle_t): Define.
(transform_view::_Iterator, zip_transform_view::_Iterator)
(adjacent_tranform_view::_Iterator): Replace pointer to view
(_M_parent) with pointer to functor (_M_fun). Update constructors
to cosntruct _M_fun from *__parent->_M_fun. Define operator* and
operator[] in terms of _M_call_deref and _M_call_subscript.
* testsuite/std/ranges/adaptors/adjacent_transform/1.cc: New tests.
* testsuite/std/ranges/adaptors/transform.cc: New tests.
* testsuite/std/ranges/zip_transform/1.cc: New tests.
Reviewed-by: Patrick Palka <[email protected]>
Signed-off-by: Tomasz Kamiński <[email protected]>
---
v2:
- rename _Statefull to _Stateful
- make __func_handle<T> to __func_handle<const T> implicit, as
suggested by Hewill
- use less criptic (_Ids, _Const) when construction _Fun_handle
for adjacent_transform
- add test for empty functor that depends on *this
- add test for overload resolution selecting between static and
member operators
Tested on x86_64-linux locally. OK for trunk?
libstdc++-v3/include/std/ranges | 214 +++++++++++++++---
.../ranges/adaptors/adjacent_transform/1.cc | 97 ++++++++
.../std/ranges/adaptors/transform.cc | 160 ++++++++++++-
.../testsuite/std/ranges/zip_transform/1.cc | 101 +++++++++
4 files changed, 535 insertions(+), 37 deletions(-)
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index ae57b9a0809..e4851df804f 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -286,6 +286,138 @@ namespace ranges
operator->() const noexcept
{ return std::__addressof(_M_value); }
};
+
+ enum class _FunctorType
+ {
+ _FunctionPtr,
+ _MemberPtr,
+ _StaticOperator,
+ _Stateful,
+ };
+
+ template<typename _Fn, _FunctorType __ft>
+ struct __func_handle
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_Fn& __func) noexcept
+ : _M_ptr(std::addressof(__func))
+ { }
+
+ template<typename _Un>
+ requires (!is_const_v<_Un>) && is_same_v<const _Un, _Fn>
+ constexpr
+ __func_handle(__func_handle<_Un, __ft> __other) noexcept
+ : _M_ptr(__other._M_ptr)
+ { }
+
+ template<typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_deref(const _Iters&... __iters) const
+ noexcept(noexcept((*_M_ptr)(*__iters...)))
+ { return (*_M_ptr)(*__iters...); }
+
+ template<typename _DistType, typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_subscript(const _DistType __n, const _Iters&... __iters) const
+
noexcept(noexcept((*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...)))
+ { return (*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...); }
+
+ private:
+ _Fn* _M_ptr = nullptr;
+
+ template<typename, _FunctorType>
+ friend struct __func_handle;
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FunctorType::_FunctionPtr>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_Fn __func) noexcept
+ : _M_ptr(__func)
+ { }
+
+ template<typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_deref(const _Iters&... __iters) const
+ noexcept(noexcept(_M_ptr(*__iters...)))
+ { return _M_ptr(*__iters...); }
+
+ template<typename _DistType, typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_subscript(const _DistType __n, const _Iters&... __iters) const
+ noexcept(noexcept(_M_ptr(__iters[iter_difference_t<_Iters>(__n)]...)))
+ { return _M_ptr(__iters[iter_difference_t<_Iters>(__n)]...); }
+
+ private:
+ _Fn _M_ptr = nullptr;
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FunctorType::_MemberPtr>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(_Fn __func) noexcept
+ : _M_ptr(__func)
+ {}
+
+ template<typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_deref(const _Iters&... __iters) const
+ noexcept(noexcept(std::__invoke(_M_ptr, *__iters...)))
+ { return std::__invoke(_M_ptr, *__iters...); }
+
+ template<typename _DistType, typename... _Iters>
+ constexpr decltype(auto)
+ _M_call_subscript(const _DistType __n, const _Iters&... __iters) const
+ noexcept(noexcept(std::__invoke(_M_ptr,
__iters[iter_difference_t<_Iters>(__n)]...)))
+ { return std::__invoke(_M_ptr,
__iters[iter_difference_t<_Iters>(__n)]...); }
+
+ private:
+ _Fn _M_ptr = nullptr;
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FunctorType::_StaticOperator>
+ {
+ __func_handle() = default;
+
+ constexpr explicit
+ __func_handle(const _Fn&) noexcept
+ {}
+
+ template<typename... _Iters>
+ static constexpr decltype(auto)
+ _M_call_deref(const _Iters&... __iters)
+ noexcept(noexcept(_Fn::operator()(*__iters...)))
+ { return _Fn::operator()(*__iters...); }
+
+ template<typename _DistType, typename... _Iters>
+ static constexpr decltype(auto)
+ _M_call_subscript(_DistType __n, const _Iters&... __iters)
+
noexcept(noexcept(_Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...)))
+ { return _Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...);
}
+ };
+
+ template<typename _Fn, typename... _Iters>
+ using __func_handle_t = decltype([] {
+ using _Fd = remove_cv_t<_Fn>;
+ if constexpr (is_member_pointer_v<_Fd>)
+ return __func_handle<_Fd, _FunctorType::_MemberPtr>();
+ else if constexpr (std::is_function_v<remove_pointer_t<_Fd>>)
+ return __func_handle<_Fd, _FunctorType::_FunctionPtr>();
+ else if constexpr (requires (const _Iters&... __iters)
+ { _Fd::operator()(*__iters...); })
+ return __func_handle<_Fd, _FunctorType::_StaticOperator>();
+ else
+ return __func_handle<_Fn, _FunctorType::_Stateful>();
+ }());
} // namespace __detail
/// A view that contains exactly one element.
@@ -1874,6 +2006,10 @@ namespace views::__adaptor
private:
using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
using _Base = transform_view::_Base<_Const>;
+ using _Base_iter = iterator_t<_Base>;
+ using _Func_handle = __detail::__func_handle_t<
+ __detail::__maybe_const_t<_Const, _Fp>,
+ _Base_iter>;
static auto
_S_iter_concept()
@@ -1888,10 +2024,8 @@ namespace views::__adaptor
return input_iterator_tag{};
}
- using _Base_iter = iterator_t<_Base>;
-
_Base_iter _M_current = _Base_iter();
- _Parent* _M_parent = nullptr;
+ [[no_unique_address]] _Func_handle _M_fun;
public:
using iterator_concept = decltype(_S_iter_concept());
@@ -1904,16 +2038,20 @@ namespace views::__adaptor
_Iterator() requires default_initializable<_Base_iter> = default;
constexpr
- _Iterator(_Parent* __parent, _Base_iter __current)
- : _M_current(std::move(__current)),
- _M_parent(__parent)
+ _Iterator(_Func_handle __fun, _Base_iter __current)
+ : _M_current(std::move(__current)), _M_fun(__fun)
{ }
+ constexpr
+ _Iterator(_Parent* __parent, _Base_iter __current)
+ : _M_current(std::move(__current)), _M_fun(*__parent->_M_fun)
+ {}
+
constexpr
_Iterator(_Iterator<!_Const> __i)
requires _Const
&& convertible_to<iterator_t<_Vp>, _Base_iter>
- : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent)
+ : _M_current(std::move(__i._M_current)), _M_fun(__i._M_fun)
{ }
constexpr const _Base_iter&
@@ -1926,8 +2064,8 @@ namespace views::__adaptor
constexpr decltype(auto)
operator*() const
- noexcept(noexcept(std::__invoke(*_M_parent->_M_fun, *_M_current)))
- { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
+ noexcept(noexcept(_M_fun._M_call_deref(_M_current)))
+ { return _M_fun._M_call_deref(_M_current); }
constexpr _Iterator&
operator++()
@@ -1980,7 +2118,7 @@ namespace views::__adaptor
constexpr decltype(auto)
operator[](difference_type __n) const
requires random_access_range<_Base>
- { return std::__invoke(*_M_parent->_M_fun, _M_current[__n]); }
+ { return _M_fun._M_call_subscript(__n, _M_current); }
friend constexpr bool
operator==(const _Iterator& __x, const _Iterator& __y)
@@ -2018,17 +2156,17 @@ namespace views::__adaptor
friend constexpr _Iterator
operator+(_Iterator __i, difference_type __n)
requires random_access_range<_Base>
- { return {__i._M_parent, __i._M_current + __n}; }
+ { return {__i._M_fun, __i._M_current + __n}; }
friend constexpr _Iterator
operator+(difference_type __n, _Iterator __i)
requires random_access_range<_Base>
- { return {__i._M_parent, __i._M_current + __n}; }
+ { return {__i._M_fun, __i._M_current + __n}; }
friend constexpr _Iterator
operator-(_Iterator __i, difference_type __n)
requires random_access_range<_Base>
- { return {__i._M_parent, __i._M_current - __n}; }
+ { return {__i._M_fun, __i._M_current - __n}; }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3483. transform_view::iterator's difference is overconstrained
@@ -5126,13 +5264,21 @@ namespace views::__adaptor
class zip_transform_view<_Fp, _Vs...>::_Iterator : public __iter_cat<_Const>
{
using _Parent = __detail::__maybe_const_t<_Const, zip_transform_view>;
+ using _Fun_handle = __detail::__func_handle_t<
+ __detail::__maybe_const_t<_Const, _Fp>,
+ iterator_t<__detail::__maybe_const_t<_Const,
_Vs>>...>;
- _Parent* _M_parent = nullptr;
+ [[no_unique_address]] _Fun_handle _M_fun;
__ziperator<_Const> _M_inner;
+ constexpr
+ _Iterator(_Fun_handle __fun, __ziperator<_Const> __inner)
+ : _M_fun(__fun), _M_inner(std::move(__inner))
+ { }
+
constexpr
_Iterator(_Parent& __parent, __ziperator<_Const> __inner)
- : _M_parent(std::__addressof(__parent)), _M_inner(std::move(__inner))
+ : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner))
{ }
friend class zip_transform_view;
@@ -5150,14 +5296,14 @@ namespace views::__adaptor
constexpr
_Iterator(_Iterator<!_Const> __i)
requires _Const && convertible_to<__ziperator<false>,
__ziperator<_Const>>
- : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner))
+ : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner))
{ }
constexpr decltype(auto)
operator*() const
{
return std::apply([&](const auto&... __iters) -> decltype(auto) {
- return std::__invoke(*_M_parent->_M_fun, *__iters...);
+ return _M_fun._M_call_deref(__iters...);
}, _M_inner._M_current);
}
@@ -5213,7 +5359,7 @@ namespace views::__adaptor
operator[](difference_type __n) const requires
random_access_range<_Base<_Const>>
{
return std::apply([&]<typename... _Is>(const _Is&... __iters) ->
decltype(auto) {
- return std::__invoke(*_M_parent->_M_fun,
__iters[iter_difference_t<_Is>(__n)]...);
+ return _M_fun._M_call_subscript(__n, __iters...);
}, _M_inner._M_current);
}
@@ -5230,17 +5376,17 @@ namespace views::__adaptor
friend constexpr _Iterator
operator+(const _Iterator& __i, difference_type __n)
requires random_access_range<_Base<_Const>>
- { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner + __n); }
friend constexpr _Iterator
operator+(difference_type __n, const _Iterator& __i)
requires random_access_range<_Base<_Const>>
- { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner + __n); }
friend constexpr _Iterator
operator-(const _Iterator& __i, difference_type __n)
requires random_access_range<_Base<_Const>>
- { return _Iterator(*__i._M_parent, __i._M_inner - __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner - __n); }
friend constexpr difference_type
operator-(const _Iterator& __x, const _Iterator& __y)
@@ -5807,13 +5953,23 @@ namespace views::__adaptor
{
using _Parent = __detail::__maybe_const_t<_Const, adjacent_transform_view>;
using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+ using _Fun_handle = decltype([]<size_t...
_Ids>(std::index_sequence<_Ids...>) {
+ return __detail::__func_handle_t<
+ __detail::__maybe_const_t<_Const, _Fp>,
+ iterator_t<__detail::__maybe_const_t<(_Ids,
_Const), _Vp>>...>();
+ }(make_index_sequence<_Nm>()));
- _Parent* _M_parent = nullptr;
+ [[no_unique_address]] _Fun_handle _M_fun;
_InnerIter<_Const> _M_inner;
+ constexpr
+ _Iterator(_Fun_handle __fun, _InnerIter<_Const> __inner)
+ : _M_fun(__fun), _M_inner(std::move(__inner))
+ { }
+
constexpr
_Iterator(_Parent& __parent, _InnerIter<_Const> __inner)
- : _M_parent(std::__addressof(__parent)), _M_inner(std::move(__inner))
+ : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner))
{ }
static auto
@@ -5854,14 +6010,14 @@ namespace views::__adaptor
constexpr
_Iterator(_Iterator<!_Const> __i)
requires _Const && convertible_to<_InnerIter<false>, _InnerIter<_Const>>
- : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner))
+ : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner))
{ }
constexpr decltype(auto)
operator*() const
{
return std::apply([&](const auto&... __iters) -> decltype(auto) {
- return std::__invoke(*_M_parent->_M_fun, *__iters...);
+ return _M_fun._M_call_deref(__iters...);
}, _M_inner._M_current);
}
@@ -5913,7 +6069,7 @@ namespace views::__adaptor
operator[](difference_type __n) const requires random_access_range<_Base>
{
return std::apply([&](const auto&... __iters) -> decltype(auto) {
- return std::__invoke(*_M_parent->_M_fun, __iters[__n]...);
+ return _M_fun._M_call_subscript(__n, __iters...);
}, _M_inner._M_current);
}
@@ -5950,17 +6106,17 @@ namespace views::__adaptor
friend constexpr _Iterator
operator+(const _Iterator& __i, difference_type __n)
requires random_access_range<_Base>
- { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner + __n); }
friend constexpr _Iterator
operator+(difference_type __n, const _Iterator& __i)
requires random_access_range<_Base>
- { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner + __n); }
friend constexpr _Iterator
operator-(const _Iterator& __i, difference_type __n)
requires random_access_range<_Base>
- { return _Iterator(*__i._M_parent, __i._M_inner - __n); }
+ { return _Iterator(__i._M_fun, __i._M_inner - __n); }
friend constexpr difference_type
operator-(const _Iterator& __x, const _Iterator& __y)
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
index 772e4b3b6a0..1667c6250c5 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
@@ -113,6 +113,97 @@ test04()
static_assert( requires { x | views::pairwise_transform(move_only{}); } );
}
+struct X
+{
+ int i,j;
+ constexpr int combine(X o) const
+ { return i + o.j; }
+};
+
+template<size_t FuncSize, typename Fn>
+constexpr bool
+test05(Fn f)
+{
+ using namespace __gnu_test;
+ X x[] = {{1,500},{2,400},{3,300},{4,200},{5,100}};
+ test_range<X, random_access_iterator_wrapper> rx(x);
+
+ auto v = rx | views::pairwise_transform(f);
+ VERIFY( ranges::size(v) == 4 );
+ VERIFY( ranges::distance(v.begin(), v.end()) == 4 );
+ VERIFY( ranges::equal(v, (int[]){401,302,203,104}) );
+ VERIFY( ranges::equal(v | views::reverse, (int[]){104,203,302,401}) );
+ using R = decltype(v);
+ using It = ranges::iterator_t<R>;
+ static_assert(std::same_as<int, decltype(*ranges::begin(v))>);
+ static_assert(std::same_as<int, std::iter_value_t<It>>);
+ static_assert(sizeof(It) == 2*sizeof(rx.begin()) + FuncSize);
+ static_assert(ranges::view<R>);
+ static_assert(ranges::sized_range<R>);
+ static_assert(!ranges::common_range<R>);
+ static_assert(ranges::random_access_range<R>);
+ return true;
+}
+
+constexpr bool
+test05a()
+{
+ auto comb = [](const X& x1, const X& x2) { return x1.i + x2.j; };
+ return test05<sizeof(void*)>(comb);
+}
+
+constexpr bool
+test05b()
+{
+ auto comb = [](const X& x1, const X& x2) static { return x1.i + x2.j; };
+ return test05<0>(comb);
+}
+
+constexpr bool
+test05c()
+{
+ int(*comb)(const X&, const X&) = [](const X& x1, const X& x2) { return x1.i
+ x2.j; };
+ return test05<sizeof(void(*)())>(comb);
+}
+
+constexpr bool
+test05d()
+{
+ return test05<sizeof(int(X::*)())>(&X::combine);
+}
+
+constexpr bool
+test05e()
+{
+ struct PickStatic
+ {
+ static constexpr int
+ operator()(const X& x1, const X& x2)
+ { return x1.i + x2.j; }
+
+ constexpr int
+ operator()(int x, int y) const
+ { return x + y; };
+ };
+ return test05<0>(PickStatic{});
+}
+
+constexpr bool
+test05f()
+{
+ struct PickObject
+ {
+ constexpr int
+ operator()(const X& x1, const X& x2) const
+ { return x1.i + x2.j; }
+
+ static constexpr int
+ operator()(int x, int y)
+ { return x + y; };
+ };
+ return test05<sizeof(void*)>(PickObject{});
+}
+
int
main()
{
@@ -120,4 +211,10 @@ main()
static_assert(test02());
static_assert(test03());
test04();
+ static_assert(test05a());
+ static_assert(test05b());
+ static_assert(test05c());
+ static_assert(test05d());
+ static_assert(test05e());
+ static_assert(test05f());
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 1788db1ce8d..b0313130e26 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -18,6 +18,7 @@
// { dg-do run { target c++20 } }
#include <algorithm>
+#include <cstdint>
#include <ranges>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
@@ -28,12 +29,12 @@ using __gnu_test::random_access_iterator_wrapper;
namespace ranges = std::ranges;
namespace views = std::ranges::views;
+template<typename Fn>
void
-test01()
+test01(Fn f)
{
int x[] = {1,2,3,4,5};
- auto is_odd = [] (int i) { return i%2==1; };
- auto v = x | views::transform(is_odd);
+ auto v = x | views::transform(f);
VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) );
using R = decltype(v);
static_assert(std::same_as<bool, decltype(*ranges::begin(v))>);
@@ -42,30 +43,124 @@ test01()
static_assert(ranges::random_access_range<R>);
}
+void
+test01a()
+{
+ auto is_odd = [] (int i) { return i%2==1; };
+ test01(is_odd);
+}
+
+void
+test01b()
+{
+#if __cpp_static_call_operator >= 202207L
+ auto is_odd = [] (int i) static { return i%2==1; };
+ test01(is_odd);
+#endif
+}
+
+void
+test01c()
+{
+ bool(*is_odd)(int) = [] (int i) { return i%2==1; };
+ test01(is_odd);
+}
+
struct X
{
int i,j;
+ int& first() { return i; }
};
+template<size_t FuncSize, typename Fn>
void
-test02()
+test02(Fn f)
{
X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}};
test_range<X, random_access_iterator_wrapper> rx(x);
- auto v = rx | views::transform(&X::i);
+ auto v = rx | views::transform(f);
VERIFY( ranges::size(v) == 5 );
VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) );
VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) );
using R = decltype(v);
+ using It = ranges::iterator_t<R>;
static_assert(std::same_as<int&, decltype(*ranges::begin(v))>);
- static_assert(std::same_as<int, std::iter_value_t<ranges::iterator_t<R>>>);
+ static_assert(std::same_as<int, std::iter_value_t<It>>);
+ static_assert(sizeof(It) == sizeof(rx.begin()) + FuncSize);
static_assert(ranges::view<R>);
static_assert(ranges::sized_range<R>);
static_assert(!ranges::common_range<R>);
static_assert(ranges::random_access_range<R>);
}
+void
+test02a()
+{ test02<sizeof(int X::*)>(&X::i); }
+
+void
+test02b()
+{ test02<sizeof(int(X::*)())>(&X::first); }
+
+void
+test02c()
+{
+ auto first = [](X& x) -> int& { return x.i; };
+ test02<sizeof(void*)>(first);
+}
+
+void
+test02d()
+{
+#if __cpp_static_call_operator >= 202207L
+ auto first = [](X& x) static -> int& { return x.i; };
+ test02<0>(first);
+#endif
+}
+
+void
+test02e()
+{
+ int&(*fptr)(X&) = [](X& x) -> int& { return x.i; };
+ test02<sizeof(void(*)())>(fptr);
+}
+
+void
+test02f()
+{
+#if __cpp_static_call_operator >= 202207L
+ struct PickStatic
+ {
+ static constexpr int&
+ operator()(X& x)
+ { return x.i; }
+
+ constexpr int
+ operator()(char*) const
+ { return 0; };
+ };
+ test02<0>(PickStatic{});
+#endif
+}
+
+void
+test02g()
+{
+#if __cpp_static_call_operator >= 202207L
+ struct PickObject
+ {
+ constexpr int&
+ operator()(X& x) const
+ { return x.i; }
+
+ static constexpr int
+ operator()(char*)
+ { return 0; };
+ };
+ test02<sizeof(void*)>(PickObject{});
+#endif
+}
+
void
test03()
{
@@ -227,11 +322,58 @@ test11()
static_assert(std::same_as<cat, std::random_access_iterator_tag>);
}
+void
+test12()
+{
+ struct Obfuscate
+ {
+ int operator()(int x) const
+ { return x + reinterpret_cast<std::uintptr_t>(this); }
+ };
+
+ int x[]{1, 2, 3, 4, 5};
+ auto v = x | views::transform(Obfuscate{});
+ VERIFY( ranges::equal(v, v) );
+};
+
+void
+test13()
+{
+#if __cpp_static_call_operator >= 202207L
+ struct StaticWins {
+ static int operator()(int i) { return 0; }
+ int operator()(float f) const { return 1; }
+ };
+
+ int x[]{1, 2, 3, 4, 5};
+ auto vs = x | views::transform(StaticWins{});
+ VERIFY( vs.front() == 0 );
+ static_assert( sizeof(vs.begin()) == sizeof(int*) );
+
+ struct MemberWins {
+ static int operator()(float f) { return 0; }
+ int operator()(int i) const { return 1; }
+ };
+
+ auto vm = x | views::transform(MemberWins{});
+ VERIFY( vm.front() == 1 );
+ static_assert( sizeof(vm.begin()) > sizeof(int*) );
+#endif
+}
+
int
main()
{
- test01();
- test02();
+ test01a();
+ test01b();
+ test01c();
+ test02a();
+ test02b();
+ test02c();
+ test02d();
+ test02e();
+ test02f();
+ test02g();
test03();
test04();
test05();
@@ -241,4 +383,6 @@ main()
test09();
test10();
test11();
+ test12();
+ test13();
}
diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
index 9a0ad3814e6..d4bd7db26cb 100644
--- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
@@ -132,6 +132,97 @@ test04()
static_assert( requires { views::zip_transform(move_only{}, x, x); } );
}
+struct X
+{
+ int i;
+ constexpr int add(int b) const
+ { return i+b; }
+};
+
+template<size_t ExtraSize, typename Fn>
+constexpr bool
+test05(Fn f)
+{
+ using namespace __gnu_test;
+ X x[] = {{1},{2},{3},{4},{5}};
+ int y[] = {500,400,300,200,100};
+ test_range<X, random_access_iterator_wrapper> rx(x);
+ test_range<int, random_access_iterator_wrapper> ry(y);
+
+ auto v = views::zip_transform(f, rx, ry);
+ VERIFY( ranges::size(v) == 5 );
+ VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
+ VERIFY( ranges::equal(v, (int[]){501,402,303,204,105}) );
+ VERIFY( ranges::equal(v | views::reverse, (int[]){105,204,303,402,501}) );
+ using R = decltype(v);
+ using It = ranges::iterator_t<R>;
+ static_assert(std::same_as<int, decltype(*ranges::begin(v))>);
+ static_assert(std::same_as<int, std::iter_value_t<It>>);
+ static_assert(sizeof(It) == sizeof(rx.begin()) + sizeof(ry.begin()) +
ExtraSize);
+ static_assert(ranges::view<R>);
+ static_assert(ranges::sized_range<R>);
+ static_assert(ranges::common_range<R>);
+ static_assert(ranges::random_access_range<R>);
+ return true;
+}
+
+constexpr bool
+test05a()
+{
+ auto add = [](const X& x, int v) { return x.i + v; };
+ return test05<sizeof(void*)>(add);
+}
+
+constexpr bool
+test05b()
+{
+ auto add = [](const X& x, int v) static { return x.i + v; };
+ return test05<0>(add);
+}
+
+constexpr bool
+test05c()
+{
+ int(*ptr)(const X&, int) = [](const X& x, int v) { return x.i + v; };
+ return test05<sizeof(void(*)())>(ptr);
+}
+
+constexpr bool
+test05d()
+{ return test05<sizeof(int(X::*)())>(&X::add); }
+
+constexpr bool
+test05e()
+{
+ struct PickStatic
+ {
+ static constexpr int
+ operator()(const X& x1, int v)
+ { return x1.i + v; }
+
+ constexpr int
+ operator()(int x, int y) const
+ { return x + y; };
+ };
+ return test05<0>(PickStatic{});
+}
+
+constexpr bool
+test05f()
+{
+ struct PickObject
+ {
+ constexpr int
+ operator()(const X& x1, int v) const
+ { return x1.i + v; }
+
+ static constexpr int
+ operator()(int x, int y)
+ { return x + y; };
+ };
+ return test05<sizeof(void*)>(PickObject{});
+}
+
int
main()
{
@@ -139,4 +230,14 @@ main()
static_assert(test02());
static_assert(test03());
test04();
+ static_assert(test01());
+ static_assert(test02());
+ static_assert(test03());
+ test04();
+ static_assert(test05a());
+ static_assert(test05b());
+ static_assert(test05c());
+ static_assert(test05d());
+ static_assert(test05e());
+ static_assert(test05f());
}
--
2.51.0