https://gcc.gnu.org/g:ac45382d0c06bc1a90ce74e0379e52496ab3bffe
commit r16-5370-gac45382d0c06bc1a90ce74e0379e52496ab3bffe Author: Tomasz Kamiński <[email protected]> Date: Fri Oct 24 16:45:59 2025 +0200 libstdc++: Fix construction function_ref from nontype<&S::x> and reference_wrapper [PR121858] To reduce instantiation count, function_ref(nontype<&S::x>, r) previously reused the invoker from function_ref(nontype<&S::x>, &r). This assumed r was always a reference to S or a derived class. However, this constructor is also valid for lvalues (but not rvalues) of reference_wrapper specializations. This patch fixes this by limiting above optimization only to situations, when argument is not specialization of reference_wrapper. This is achieved bu comparing __inv_unwrap<_Td>::type with _Td. We use __inv_unwrap because unwrap_reference_t does not handle cv-qualified types. PR libstdc++/121858 libstdc++-v3/ChangeLog: * include/bits/funcref_impl.h (function_ref::function_ref(nontype<__fn>, _Up&&)): Handle. reference_wrapper. * testsuite/20_util/function_ref/call.cc: Call and update test05(). Add new test06() for reference_wrapper. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/bits/funcref_impl.h | 6 +- .../testsuite/20_util/function_ref/call.cc | 115 +++++++++++++++++---- 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/libstdc++-v3/include/bits/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h index 44c992281be2..3fe2fb1b3f52 100644 --- a/libstdc++-v3/include/bits/funcref_impl.h +++ b/libstdc++-v3/include/bits/funcref_impl.h @@ -138,13 +138,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) static_assert(__fn != nullptr); - using _Tr = _Td _GLIBCXX_MOF_CV&; - if constexpr (is_member_pointer_v<_Fn> && is_lvalue_reference_v<_Tr>) + if constexpr (is_member_pointer_v<_Fn> + && same_as<_Td, typename __inv_unwrap<_Td>::type>) // N.B. invoking member pointer on lvalue produces the same effects, // as invoking it on pointer to that lvalue. _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; else - _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>; + _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Td _GLIBCXX_MOF_CV&>; _M_init(std::addressof(__ref)); } diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc index 23253c33ee39..49c6030b221a 100644 --- a/libstdc++-v3/testsuite/20_util/function_ref/call.cc +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc @@ -130,7 +130,6 @@ int callback_ref(ftype& f, int x) { return f(x); } void test05() { - function_ref<int(int)> r1(nontype<&callback_ptr>, &twice); VERIFY( r1(2) == 4 ); function_ref<int(int)> r2(nontype<&callback_ptr>, cube); @@ -140,27 +139,103 @@ test05() VERIFY( r3(3) == 6 ); function_ref<int(int)> r4(nontype<&callback_ref>, cube); VERIFY( r4(3) == 27 ); +} - // Checks if distinction between reference and pointer - // is preserved. - struct F - { - static - int operator()(ftype* f, int x) - { return f(x) + 1000; } +void +test06() +{ + struct S { + int v; + int& m() { return v; } + const int& c() const { return v; } - static - int operator()(ftype& f, int x) - { return f(x) + 2000; } + int& operator()(int) { return v; } + int const& operator()(int, int) const { return v; } }; - function_ref<int(int)> r5(nontype<F{}>, &twice); - VERIFY( r5(2) == 1004 ); - function_ref<int(int)> r6(nontype<F{}>, twice); - VERIFY( r6(2) == 2008 ); - function_ref<int(int)> r7(nontype<F{}>, &cube); - VERIFY( r7(3) == 1006 ); - function_ref<int(int)> r8(nontype<F{}>, cube); - VERIFY( r8(3) == 2027 ); + S s{10}; + std::reference_wrapper<S> sr(s); + std::reference_wrapper<const S> csr(s); + + std::function_ref<int&(int)> e1(sr); + VERIFY( &e1(0) == &s.v ); + std::function_ref<int&(int) const> e2(sr); + VERIFY( &e2(0) == &s.v ); + std::function_ref<int&(int) const> e3(std::as_const(sr)); + VERIFY( &e3(0) == &s.v ); + + std::function_ref<const int&(int, int)> e4(sr); + VERIFY( &e4(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e5(sr); + VERIFY( &e5(0, 0) == &s.v ); + std::function_ref<const int&(int, int)> e6(csr); + VERIFY( &e6(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e7(csr); + VERIFY( &e7(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e8(std::as_const(csr)); + VERIFY( &e8(0, 0) == &s.v ); + + std::function_ref<int&()> f1(std::nontype<&S::v>, sr); + VERIFY( &f1() == &s.v ); + std::function_ref<const int&()> f2(std::nontype<&S::v>, sr); + VERIFY( &f2() == &s.v ); + std::function_ref<int&()> f3(std::nontype<&S::m>, sr); + VERIFY( &f3() == &s.v ); + std::function_ref<const int&()> f4(std::nontype<&S::c>, sr); + VERIFY( &f4() == &s.v ); + + std::function_ref<const int&()> f5(std::nontype<&S::v>, csr); + VERIFY( &f5() == &s.v ); + std::function_ref<const int&()> f6(std::nontype<&S::c>, sr); + VERIFY( &f6() == &s.v ); + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::c>, std::reference_wrapper<S>&> + ); + + std::function_ref<int&()> f7(std::nontype<&S::v>, std::as_const(sr)); + VERIFY( &f7() == &s.v ); + std::function_ref<const int&()> f8(std::nontype<&S::m>, std::as_const(sr)); + VERIFY( &f8() == &s.v ); + + // No rvalue reference_wrapper support + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::v>, std::reference_wrapper<S>> + ); + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::v>, std::reference_wrapper<const S>> + ); + + // reference to reference_wrapper are bound, so mutation are visible + S s2{20}; + sr = s2; + csr = s2; + VERIFY( &e1(0) == &s2.v ); + VERIFY( &e2(0) == &s2.v ); + VERIFY( &e3(0) == &s2.v ); + VERIFY( &e4(0, 0) == &s2.v ); + VERIFY( &e5(0, 0) == &s2.v ); + VERIFY( &e6(0, 0) == &s2.v ); + VERIFY( &e7(0, 0) == &s2.v ); + VERIFY( &e8(0, 0) == &s2.v ); + VERIFY( &f1() == &s2.v ); + VERIFY( &f2() == &s2.v ); + VERIFY( &f3() == &s2.v ); + VERIFY( &f4() == &s2.v ); + VERIFY( &f5() == &s2.v ); + VERIFY( &f6() == &s2.v ); + VERIFY( &f7() == &s2.v ); + VERIFY( &f8() == &s2.v ); + + constexpr auto id = []<typename T>(const std::reference_wrapper<T>& x) + { return &x; }; + + // identity of reference_wrapper is preserved + std::function_ref<const std::reference_wrapper<S>*()> g1(std::nontype<id>, sr); + VERIFY( g1() == &sr ); + std::function_ref<const std::reference_wrapper<const S>*()> g2(std::nontype<id>, csr); + VERIFY( g2() == &csr ); } struct Incomplete; @@ -182,5 +257,7 @@ int main() test02(); test03(); test04(); + test05(); + test06(); test_params(); }
