The iterators for transform views (views::transform, views::zip_transform,
and views::adjacent_transform) now store a function handle from (from
__detail::__func_handle namespace) instead of a pointer to the view object
(_M_parent).
The following handle templates are defined in __func_handle namespace:
* _Inplace: Used if the functor is a function pointer or standard operator
wrapper (std::less<>, etc). The functor is stored directly in __func_handle
and the iterator. This avoid double indirection through a pointer to the
function pointer, and reduce the size of iterator for std wrappers.
* _InplaceMemPtr: Used for data or function member pointers. This behaves
similarly to _Inplace, but uses __invoke for invocations.
* _StaticCall: 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.
* _ViaPointer: Used for all remaining cases. __func_handle stores a pointer
to the functor object stored within the view. Only for this template the
cv-qualification of the functor template parameter (_Fn) relevant, and
specialization for both const and mutable types are generated.
As a consequence of these changes, the iterators of transform views no longer
depend on the view object when handle other than __func_handle::_ViaPointer
is used. The corresponding views are not marked as borrowed_range, as they
are not marked as such in the standard.
The use of _Inplace is limited to only set of pre-C++20 standard functors,
as for once introduced for or later operator() was retroactively made static.
We do not extent to to any empty fuctor, as it's oprator may still depend on
value of this pointer as illustrated by test12 in
std/ranges/adaptors/transform.cc test file.
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 handles 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::__is_std_op_template)
(__detail::__is_std_op_wrapper, __func_handle::_Inplace)
(__func_handle::_InplaceMemPtr, __func_handle::_ViaPointer)
(__func_handle::_StaticCall, __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]>
---
v3 aims to reduce cost of template specialization maatching, by:
* implementing __is_std_op_wrapper in terms of __is_std_op_template
that has explicit specialization for each operator wrapper,
* defining separate specialization for each functor handle, instead
of partial specialization. They are defined in __func_handle namespace
to emphasize that they are related to each other.
Tested on x86_64-linux. OK for trunk?
libstdc++-v3/include/std/ranges | 261 ++++++++++++++++--
.../ranges/adaptors/adjacent_transform/1.cc | 41 +++
.../std/ranges/adaptors/transform.cc | 178 +++++++++++-
.../testsuite/std/ranges/zip_transform/1.cc | 97 +++++++
4 files changed, 540 insertions(+), 37 deletions(-)
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index ae57b9a0809..7c5ac931e31 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -286,6 +286,185 @@ namespace ranges
operator->() const noexcept
{ return std::__addressof(_M_value); }
};
+
+ template<template<typename> class>
+ constexpr bool __is_std_op_template = false;
+
+ template<>
+ inline constexpr bool __is_std_op_template<std::equal_to> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::not_equal_to> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::greater> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::less> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::greater_equal> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::less_equal> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::plus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::minus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::multiplies> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::divides> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::modulus> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::negate> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_and> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_or> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::logical_not> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_and> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_or> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_xor> = true;
+ template<>
+ inline constexpr bool __is_std_op_template<std::bit_not> = true;
+
+
+ template<typename _Fn>
+ constexpr bool __is_std_op_wrapper = false;
+
+ template<template<typename> class _Ft, typename _Tp>
+ constexpr bool __is_std_op_wrapper<_Ft<_Tp>>
+ = __is_std_op_template<_Ft>;
+
+ namespace __func_handle
+ {
+ template<typename _Fn>
+ struct _Inplace
+ {
+ _Inplace() = default;
+
+ constexpr explicit
+ _Inplace(_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:
+ [[no_unique_address]] _Fn _M_ptr = _Fn();
+ };
+
+ template<typename _Fn>
+ struct _InplaceMemPtr
+ {
+ _InplaceMemPtr() = default;
+
+ constexpr explicit
+ _InplaceMemPtr(_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 _ViaPointer
+ {
+ _ViaPointer() = default;
+
+ constexpr explicit
+ _ViaPointer(_Fn& __func) noexcept
+ : _M_ptr(std::addressof(__func))
+ { }
+
+ template<typename _Un>
+ requires (!is_const_v<_Un>) && is_same_v<const _Un, _Fn>
+ constexpr
+ _ViaPointer(_ViaPointer<_Un> __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>
+ friend struct _ViaPointer;
+ };
+
+ template<typename _Fn>
+ struct _StaticCall
+ {
+ _StaticCall() = default;
+
+ constexpr explicit
+ _StaticCall(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)]...); }
+ };
+ } // __func_handle
+
+ 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::_InplaceMemPtr<_Fd>();
+ else if constexpr (is_function_v<remove_pointer_t<_Fd>>)
+ return __func_handle::_Inplace<_Fd>();
+ else if constexpr (__is_std_op_wrapper<_Fd>)
+ return __func_handle::_Inplace<_Fd>();
+ else if constexpr (requires (const _Iters&... __iters)
+ { _Fd::operator()(*__iters...); })
+ return __func_handle::_StaticCall<_Fd>();
+ else
+ return __func_handle::_ViaPointer<_Fn>();
+ }());
} // namespace __detail
/// A view that contains exactly one element.
@@ -1874,6 +2053,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 +2071,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 +2085,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 +2111,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 +2165,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 +2203,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 +5311,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 +5343,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 +5406,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 +5423,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 +6000,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 +6057,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 +6116,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 +6153,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..6890618754b 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,47 @@ test04()
static_assert( requires { x | views::pairwise_transform(move_only{}); } );
}
+template<size_t FuncSize, typename Fn>
+void
+test05(Fn f)
+{
+ int x[] = {1,2,3,4,5,6};
+ auto v = x | views::pairwise_transform(f);
+ static_assert(sizeof(v.begin()) == 2*sizeof(int*) + FuncSize);
+}
+
+void
+test05all()
+{
+ test05<0>(std::equal_to<>());
+ test05<0>(std::equal_to<>());
+ test05<0>(std::not_equal_to<>());
+ test05<0>(std::greater<>());
+ test05<0>(std::less<>());
+ test05<0>(std::greater_equal<>());
+ test05<0>(std::less_equal<>());
+
+ test05<0>(std::ranges::equal_to());
+ test05<0>(std::ranges::not_equal_to());
+ test05<0>(std::ranges::greater());
+ test05<0>(std::ranges::less());
+ test05<0>(std::ranges::greater_equal());
+ test05<0>(std::ranges::less_equal());
+
+ test05<0>(std::plus<>());
+ test05<0>(std::minus<>());
+ test05<0>(std::multiplies<>());
+ test05<0>(std::divides<>());
+ test05<0>(std::modulus<>());
+
+ test05<0>(std::logical_and<>());
+ test05<0>(std::logical_or<>());
+
+ test05<0>(std::bit_and<>());
+ test05<0>(std::bit_or<>());
+ test05<0>(std::bit_xor<>());
+}
+
int
main()
{
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 1788db1ce8d..3a21a1f0f9c 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,75 @@ 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
+}
+
+template<size_t FuncSize, typename Fn>
+void
+test14(Fn f)
+{
+ int x[] = {1,2,3,4,5,6};
+ auto v = x | views::transform(std::negate<>());
+ static_assert(sizeof(v.begin()) == sizeof(int*) + FuncSize);
+}
+
+void
+test14all()
+{
+ test14<0>(std::identity());
+ test14<0>(std::negate<>());
+ test14<0>(std::bit_not<>());
+}
+
int
main()
{
- test01();
- test02();
+ test01a();
+ test01b();
+ test01c();
+ test02a();
+ test02b();
+ test02c();
+ test02d();
+ test02e();
+ test02f();
+ test02g();
test03();
test04();
test05();
@@ -241,4 +400,7 @@ main()
test09();
test10();
test11();
+ test12();
+ test13();
+ test14all();
}
diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
index 9a0ad3814e6..8524a146eaa 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,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());
}
--
2.51.1