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 unwrapping the reference_wrapper<T> within the
nontype<f> constructor when f is a member pointer. This is achieved by casting
the argument to typename __inv_unwrap<_Td>::type _GLIBCXX_MOF_CV&. This results
in U& if _Td is a (possibly-const) reference_wrapper<U>, and _Td 
_GLIBCXX_MOF_CV&
otherwise. We use __inv_unwrap because unwrap_reference_t does not handle
cv-qualified types.

Consequently, a function_ref constructed from nontype<&S::x> and a
reference_wrapper (rw) now refers to rw.get() instead of the rw object itself,
mitigating the risk of dangling references.

Note that reference_wrapper arguments cannot be unwrapped in the general case,
s the functor f referenced by nontype<f> may explicitly accept 
reference_wrapper<T>&
as a parameter to observe identity or modify the argument.

        PR libstdc++/121858

libstdc++-v3/ChangeLog:

        * include/bits/funcref_impl.h
        (function_ref::function_ref(nontype<__fn>, _Up&&)): Unwrap
        reference_wrapper.
        * testsuite/20_util/function_ref/call.cc: Call and update
        test05(). Add new test06() for reference_wrapper.
---
Testing on x88_64-linux. OK for trunk?

 libstdc++-v3/include/bits/funcref_impl.h      | 21 ++++--
 .../testsuite/20_util/function_ref/call.cc    | 75 ++++++++++++++-----
 2 files changed, 69 insertions(+), 27 deletions(-)

diff --git a/libstdc++-v3/include/bits/funcref_impl.h 
b/libstdc++-v3/include/bits/funcref_impl.h
index 44c992281be..cc510aba3a6 100644
--- a/libstdc++-v3/include/bits/funcref_impl.h
+++ b/libstdc++-v3/include/bits/funcref_impl.h
@@ -138,14 +138,21 @@ _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>)
-           // 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>;
+         if constexpr (is_member_pointer_v<_Fn>)
+           {
+             // is_invocable_v with member pointer is only true for reference
+             // to class (or derived from it) or reference wrapper to such,
+             // we instead store pointer to referenced object
+             using _Ur = typename __inv_unwrap<_Td>::type _GLIBCXX_MOF_CV&;
+             _Ur __uref = __ref;
+             _M_invoke = &_Invoker::template _S_bind_ptr<__fn, 
remove_reference_t<_Ur>>;
+             _M_init(std::addressof(__uref));
+           }
          else
-           _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>;
-         _M_init(std::addressof(__ref));
+           {
+             _M_invoke = &_Invoker::template _S_bind_ref<__fn, _Td 
_GLIBCXX_MOF_CV&>;
+             _M_init(std::addressof(__ref));
+           }
        }
 
       /// Target object is equivalent to std::bind_front<_fn>(__ptr).
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc 
b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
index 23253c33ee3..d986e94b7bf 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,61 @@ 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; }
-
-    static
-    int operator()(ftype& f, int x)
-    { return f(x) + 2000; }
+void
+test06()
+{
+  struct S { 
+   int v;
+   int& m() { return v; }
+   const int& c() 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<S> csr(s);
+
+  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 wrapepr 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>>
+  );
+
+  constexpr auto id = [](const std::reference_wrapper<S>& x)
+  { return &x; };
+
+  // identity of reference_wrapper is preserved
+  std::function_ref<const std::reference_wrapper<S>*()> f9(std::nontype<id>, 
sr);
+  VERIFY( f9() == &sr );
+  std::function_ref<const std::reference_wrapper<S>*()> f10(std::nontype<id>, 
csr);
+  VERIFY( f10() == &csr );
 }
 
 struct Incomplete;
@@ -182,5 +215,7 @@ int main()
   test02();
   test03();
   test04();
+  test05();
+  test06();
   test_params();
 }
-- 
2.51.0

Reply via email to