On Tue, 18 Nov 2025 at 08:12, Tomasz Kamiński <[email protected]> wrote:
>
> 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.
> ---
> v3 only expands test for rebinding.
>
> Tested on x86_64-linux. OK for trunk?

OK


>
>  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 44c992281be..3fe2fb1b3f5 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 23253c33ee3..49c6030b221 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();
>  }
> --
> 2.51.1
>

Reply via email to