From 37aebac75e37187bd96612bccf230adcd206c221 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 19 Nov 2023 01:36:48 +0800 Subject: [PATCH] Make range adaptor closure objects perfect forwarding --- stl/inc/ranges | 25 ++++--- .../test.cpp | 75 +++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b71c8e8231f..dfafd153bd7 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -133,34 +133,41 @@ namespace ranges { is_nothrow_constructible_v<_Ty1, _ClosureLeft>&& is_nothrow_constructible_v<_Ty2, _ClosureRight>) : _Left(_STD forward<_Ty1>(_Val1)), _Right(_STD forward<_Ty2>(_Val2)) {} + void operator()(auto&&) & = delete; + void operator()(auto&&) const& = delete; + void operator()(auto&&) && = delete; + void operator()(auto&&) const&& = delete; + + // TRANSITION, DevCom-10519736, explicit `this->` shouldn't be needed. + template - _NODISCARD constexpr auto operator()(_Ty&& _Val) & noexcept( + _NODISCARD constexpr decltype(auto) operator()(_Ty&& _Val) & noexcept( noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) - requires requires { _Right(_Left(_STD forward<_Ty>(_Val))); } + requires requires { this->_Right(this->_Left(_STD forward<_Ty>(_Val))); } { return _Right(_Left(_STD forward<_Ty>(_Val))); } template - _NODISCARD constexpr auto operator()(_Ty&& _Val) const& noexcept( + _NODISCARD constexpr decltype(auto) operator()(_Ty&& _Val) const& noexcept( noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) - requires requires { _Right(_Left(_STD forward<_Ty>(_Val))); } + requires requires { this->_Right(this->_Left(_STD forward<_Ty>(_Val))); } { return _Right(_Left(_STD forward<_Ty>(_Val))); } template - _NODISCARD constexpr auto operator()(_Ty&& _Val) && noexcept( + _NODISCARD constexpr decltype(auto) operator()(_Ty&& _Val) && noexcept( noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) - requires requires { _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } + requires requires { _STD move(this->_Right)(_STD move(this->_Left)(_STD forward<_Ty>(_Val))); } { return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } template - _NODISCARD constexpr auto operator()(_Ty&& _Val) const&& noexcept( + _NODISCARD constexpr decltype(auto) operator()(_Ty&& _Val) const&& noexcept( noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) - requires requires { _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } + requires requires { _STD move(this->_Right)(_STD move(this->_Left)(_STD forward<_Ty>(_Val))); } { return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } @@ -180,7 +187,7 @@ namespace ranges { _EXPORT_STD template requires (_Range_adaptor_closure_object<_Right> && range<_Left>) - _NODISCARD constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept( + _NODISCARD constexpr decltype(auto) operator|(_Left&& __l, _Right&& __r) noexcept( noexcept(_STD forward<_Right>(__r)(_STD forward<_Left>(__l)))) requires requires { static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l)); } { diff --git a/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp index 00926c5635c..f1e4bddfe8e 100644 --- a/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp +++ b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp @@ -223,6 +223,81 @@ constexpr bool test_mixing_of_range_adaptors() { return true; } +struct Pinned { + Pinned() = default; + Pinned(const Pinned&) = delete; + Pinned& operator=(const Pinned&) = delete; +}; + +constexpr Pinned pinned_object{}; + +struct PinnedReturningRaco : ranges::range_adaptor_closure { + constexpr const Pinned& operator()(ranges::range auto&&) const noexcept { + return pinned_object; + } +}; + +struct RangeIdentity : ranges::range_adaptor_closure { + constexpr auto&& operator()(ranges::range auto&& t) const noexcept { + return forward(t); + } +}; + +struct ConstOnlyRangeIdentity : ranges::range_adaptor_closure { + constexpr auto&& operator()(ranges::range auto&& t) const noexcept { + return forward(t); + } + void operator()(ranges::range auto&&) = delete; +}; + +void test_perfect_forwarding_properties() { // COMPILE-ONLY + // GH-4153: : operator|(_Left&& __l, _Right&& __r) should return decltype(auto) + // Intentionally avoid using some traits/concepts to test correctness in non-immediate contexts. + { + PinnedReturningRaco raco{}; + + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + } + { + + auto combined_pipeline = RangeIdentity{} | PinnedReturningRaco{}; + + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + } + { + auto weird_pipeline = ConstOnlyRangeIdentity{} | PinnedReturningRaco{}; + using WeirdPipelineType = decltype(weird_pipeline); + + static_assert(same_as); + static_assert(same_as); + + static_assert(!CanPipe); + static_assert(!CanPipe); + + static_assert(same_as); + static_assert(same_as); + + static_assert(!invocable); + static_assert(!invocable); + } +} + int main() { assert(test_user_defined_adaptors()); static_assert(test_user_defined_adaptors());