We might also need is_default_constructible_v. For example, a singleton satisfies is_empty_v<F> && copyable<F> && trivially_copyable<F>, but it is not a default-constructible. Otherwise, the forward iterator will not be default-constructible and will no longer be a forward iterator. I personally think that determining the condition for storing functors inside iterators is actually difficult.
Tomasz Kaminski <[email protected]> 於 2025年11月15日 週六 上午1:37寫道: > From other side, it would be fairly easy to add another _TrivlalEmpty > specialization for __func_handle, > that would be generated under p3117r1 conditions, which I do not like, as > they do not make even *it > on same iterator sable for the Obfuscating symbol. > I would go instead with is_empty_v<F> && copyable<F> && > trivially_copyable<F>, and have [[no_unique_address]] F _M_fun > member. > > From that perspective, think we should land this patch regardless. Even if > we would like to do a follow up aligned with > additional specialization that is aligned the paper. > > > Regards, > Tomasz > > On Fri, Nov 14, 2025 at 3:37 PM Tomasz Kaminski <[email protected]> > wrote: > >> >> >> On Fri, Nov 14, 2025 at 2:18 PM Hewill Kang <[email protected]> wrote: >> >>> I can't help but wonder, if this patch can't optimize for lambdas (users >>> generally don't explicitly spell `static`), then I don't see much point in >>> this optimization. >>> >> Then they should start to. if they want to get optimizations. For most >> cases it really does not matter, as the compiler inlines and removes all >> iterators. >> The only relevant cases here are erasing view (storing in any_view) or >> relying on range being borrowed. >> >>> >>> I think the case of `Obfuscate` is unlikely to occur. The view class >>> will move the original object to the `movable_box` via `std::move(fun)`, so >>> `this` pointer has already changed. >>> >> I believe the standard requires it to work, so breaking it would be >> invalid optimization. >> >>> >>> If we could actually create such a pretentious functor with a disgusting >>> operator(), I think the standard has every reason not to care about it. >>> >>> Therefore, after thinking about it, I think we should perhaps follow >>> p3117r1 and use `is_empty_v<F> && is_trivially_default_constructible_v<F >>> > && is_trivially_destructible_v<F>` to optimize lambda. Because that >>> is undoubtedly the most common use case (and the most worthy of >>> optimization). This is something this patch should be interested in. >>> >> Unless the standard gives permission to do so, I do not think such change >> is possible. >> >>> >>> Tomasz Kaminski <[email protected]> 於 2025年11月14日 週五 下午5:36寫道: >>> >>>> >>>> >>>> On Fri, Nov 14, 2025 at 10:24 AM Hewill Kang <[email protected]> wrote: >>>> >>>>> Yes, I do not think anything in standard clearly states the that >>>>>> following is >>>>>> allowed to fail: >>>>>> struct Obfuscate >>>>>> { >>>>>> int operator()(int x) const >>>>>> { return x + static_assert<unintptr_t>(this); } >>>>>> }; >>>>> >>>>> >>>>>> std::vector<int> v{1, 2, 3, 4, 5}; >>>>>> auto tv = v | std::transform(Obfuscate{}); >>>>>> VERIFY( ranges::equal(tv, tv) ); >>>>>> But storing an empty object in an iterator will break. >>>>> >>>>> >>>>> This is a very good observation, so recreating empty objects is still >>>>> problematic. >>>>> It also seems to suggest that >>>>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3117r1.html >>>>> (most >>>>> likely a follow-up to this patch) and >>>>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3705r1.html >>>>> might have issues with this. >>>>> For example, `operator()` uses `this` or `operator==` uses `this`. >>>>> >>>> Yes, static operator() is bulletproof way of ensuring that it does not >>>> depend on object identity, >>>> even if the object itself is not empty. We do not need to use >>>> heuristics based on size anymore. >>>> >>>>> >>>>> Tomasz Kaminski <[email protected]> 於 2025年11月14日 週五 下午2:36寫道: >>>>> >>>>>> >>>>>> >>>>>> On Fri, Nov 14, 2025 at 4:16 AM Patrick Palka <[email protected]> >>>>>> wrote: >>>>>> >>>>>>> On Thu, 13 Nov 2025, Tomasz Kamiński wrote: >>>>>>> >>>>>>> > 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. >>>>>>> > * _Statefull: 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. >>>>>>> >>>>>>> FWIW it's spelled 'stateful' with just one l. >>>>>>> >>>>>>> I guess you concluded that this optimization is unsafe for empty >>>>>>> objects >>>>>>> with non-static operator()? >>>>>>> >>>>>> Yes, I do not think anything in standard clearly states the that >>>>>> following is >>>>>> allowed to fail: >>>>>> struct Obfuscate >>>>>> { >>>>>> int operator()(int x) const >>>>>> { return x + static_assert<unintptr_t>(this); } >>>>>> }; >>>>>> >>>>>> std::vector<int> v{1, 2, 3, 4, 5}; >>>>>> auto tv = v | std::transform(Obfuscate{}); >>>>>> VERIFY( ranges::equal(tv, tv) ); >>>>>> >>>>>> But storing an empty object in an iterator will break. >>>>>> >>>>>> >>>>>>> > >>>>>>> > 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 _Statefull. 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. >>>>>>> > --- >>>>>>> > Tested transform view files locally. More testing in progress. >>>>>>> > >>>>>>> > libstdc++-v3/include/std/ranges | 214 >>>>>>> +++++++++++++++--- >>>>>>> > .../ranges/adaptors/adjacent_transform/1.cc | 97 ++++++++ >>>>>>> > .../std/ranges/adaptors/transform.cc | 118 +++++++++- >>>>>>> > .../testsuite/std/ranges/zip_transform/1.cc | 101 +++++++++ >>>>>>> > 4 files changed, 493 insertions(+), 37 deletions(-) >>>>>>> > >>>>>>> > diff --git a/libstdc++-v3/include/std/ranges >>>>>>> b/libstdc++-v3/include/std/ranges >>>>>>> > index ae57b9a0809..28b4f1d2b90 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, >>>>>>> > + _Statefull, >>>>>>> > + }; >>>>>>> > + >>>>>>> > + 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 explicit >>>>>>> > + __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::_Statefull>(); >>>>>>> > + }()); >>>>>>> > } // 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<_Const || (_Ids < 0u), >>>>>>> _Vp>>...>(); >>>>>>> >>>>>>> Maybe '(_Ids, _Const)' would be a little less cryptic than >>>>>>> '_Const || (_Ids < 0u)'? >>>>>>> >>>>>> Yes, that is major improvement. >>>>>> >>>>>>> Besides that looks good! >>>>>>> >>>>>>> > + }(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..c6d6b916730 100644 >>>>>>> > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc >>>>>>> > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc >>>>>>> > @@ -28,12 +28,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 +42,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() >>>>>>> > { >>>>>>> > @@ -230,8 +324,16 @@ test11() >>>>>>> > int >>>>>>> > main() >>>>>>> > { >>>>>>> > - test01(); >>>>>>> > - test02(); >>>>>>> > + test01a(); >>>>>>> > + test01b(); >>>>>>> > + test01c(); >>>>>>> > + test02a(); >>>>>>> > + test02b(); >>>>>>> > + test02c(); >>>>>>> > + test02d(); >>>>>>> > + test02e(); >>>>>>> > + test02f(); >>>>>>> > + test02g(); >>>>>>> > test03(); >>>>>>> > test04(); >>>>>>> > test05(); >>>>>>> > 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 >>>>>>> > >>>>>>> > >>>>>> >>>>>>
