(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.
Signed-off-by: Tomasz Kamiński <[email protected]>
---
v2:
- stores inside of iterator selected set of standard operators
- renames the _FunctorType enum to _FuncHandleEnum and corresponding
enumertor values (_Inplace is also used for empty functors)
- do not define __func_handle primary template, and provide separate
specialization for _ViaTemplate (previously _Stateful)
- add test for all functors
Tessted on x86_64-linux linux. *range*transform* additionally tested
with all standard modes.
libstdc++-v3/include/bits/utility.h | 9 +
libstdc++-v3/include/std/format | 5 -
libstdc++-v3/include/std/ranges | 249 ++++++++++++++++--
.../ranges/adaptors/adjacent_transform/1.cc | 41 +++
.../std/ranges/adaptors/transform.cc | 185 ++++++++++++-
.../testsuite/std/ranges/zip_transform/1.cc | 101 +++++++
6 files changed, 548 insertions(+), 42 deletions(-)
diff --git a/libstdc++-v3/include/bits/utility.h
b/libstdc++-v3/include/bits/utility.h
index 96ac69883f1..522a7a1d884 100644
--- a/libstdc++-v3/include/bits/utility.h
+++ b/libstdc++-v3/include/bits/utility.h
@@ -46,6 +46,15 @@ namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
+/// @cond undocumented
+#if __cplusplus >= 201703L
+ template<typename _Tp, template<typename...> class _Class>
+ constexpr bool __is_specialization_of = false;
+ template<template<typename...> class _Class, typename... _Args>
+ constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
+#endif
+/// @endcond
+
/// Finds the size of a given tuple type.
template<typename _Tp>
struct tuple_size;
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index f64f35a202e..1e1ec1fb98f 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -417,11 +417,6 @@ namespace __format
};
/// @cond undocumented
- template<typename _Tp, template<typename...> class _Class>
- constexpr bool __is_specialization_of = false;
- template<template<typename...> class _Class, typename... _Args>
- constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
-
namespace __format
{
// pre: first != last
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index ae57b9a0809..b384e5e45d3 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -286,6 +286,173 @@ namespace ranges
operator->() const noexcept
{ return std::__addressof(_M_value); }
};
+
+ enum class _FuncHandleType
+ {
+ _Inplace,
+ _InplaceMemPtr,
+ _ViaPointer,
+ _StaticCall,
+ };
+
+ template<typename _Fn>
+ concept __is_std_op_wrapper = is_empty_v<_Fn> && (
+ is_same_v<_Fn, std::identity>
+ || __is_specialization_of<_Fn, std::equal_to>
+ || __is_specialization_of<_Fn, std::not_equal_to>
+ || __is_specialization_of<_Fn, std::greater>
+ || __is_specialization_of<_Fn, std::less>
+ || __is_specialization_of<_Fn, std::greater_equal>
+ || __is_specialization_of<_Fn, std::less_equal>
+ || is_same_v<_Fn, std::compare_three_way>
+ || is_same_v<_Fn, std::ranges::equal_to>
+ || is_same_v<_Fn, std::ranges::not_equal_to>
+ || is_same_v<_Fn, std::ranges::greater>
+ || is_same_v<_Fn, std::ranges::less>
+ || is_same_v<_Fn, std::ranges::greater_equal>
+ || is_same_v<_Fn, std::ranges::less_equal>
+ || __is_specialization_of<_Fn, std::plus>
+ || __is_specialization_of<_Fn, std::minus>
+ || __is_specialization_of<_Fn, std::multiplies>
+ || __is_specialization_of<_Fn, std::divides>
+ || __is_specialization_of<_Fn, std::modulus>
+ || __is_specialization_of<_Fn, std::negate>
+ || __is_specialization_of<_Fn, std::logical_and>
+ || __is_specialization_of<_Fn, std::logical_or>
+ || __is_specialization_of<_Fn, std::logical_not>
+ || __is_specialization_of<_Fn, std::bit_and>
+ || __is_specialization_of<_Fn, std::bit_or>
+ || __is_specialization_of<_Fn, std::bit_xor>
+ || __is_specialization_of<_Fn, std::bit_not>);
+
+ template<typename _Fn, _FuncHandleType __ft>
+ struct __func_handle;
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FuncHandleType::_Inplace>
+ {
+ __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:
+ [[no_unique_address]] _Fn _M_ptr = _Fn();
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FuncHandleType::_InplaceMemPtr>
+ {
+ __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, _FuncHandleType::_ViaPointer>
+ {
+ __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, _FuncHandleType::_ViaPointer>
__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, _FuncHandleType>
+ friend struct __func_handle;
+ };
+
+ template<typename _Fn>
+ struct __func_handle<_Fn, _FuncHandleType::_StaticCall>
+ {
+ __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, _FuncHandleType::_InplaceMemPtr>();
+ else if constexpr (is_function_v<remove_pointer_t<_Fd>>)
+ return __func_handle<_Fd, _FuncHandleType::_Inplace>();
+ else if constexpr (__is_std_op_wrapper<_Fd>)
+ return __func_handle<_Fd, _FuncHandleType::_Inplace>();
+ else if constexpr (requires (const _Iters&... __iters)
+ { _Fd::operator()(*__iters...); })
+ return __func_handle<_Fd, _FuncHandleType::_StaticCall>();
+ else
+ return __func_handle<_Fn, _FuncHandleType::_ViaPointer>();
+ }());
} // namespace __detail
/// A view that contains exactly one element.
@@ -1874,6 +2041,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 +2059,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 +2073,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 +2099,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 +2153,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 +2191,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 +5299,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 +5331,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 +5394,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 +5411,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 +5988,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 +6045,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 +6104,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 +6141,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..7a0c344cc5c 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,131 @@ 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
+test02h()
+{
+ int&(*fptr)(X&) = [](X& x) -> int& { return x.i; };
+ test02<sizeof(void(*)())>(fptr);
+}
+
void
test03()
{
@@ -227,11 +329,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 +407,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..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.1