Thanks. The patch is LGTM.

Tomasz Kaminski <[email protected]> 於 2025年10月25日 週六 上午1:35寫道:

>
>
> On Fri, Oct 24, 2025 at 7:03 PM Hewill Kang <[email protected]> wrote:
>
>> `volatile std::reference_wrapper<S>` will cause `_Ur __uref = __ref;`
>>>> hard error. But I'm not sure we really need to care about it.
>>>>
>>>>> result_of (where  __inv_unwrap is sourced from), does not care about
>>>>> it. So I would say, we do not need to.
>>>>
>>>>
>>
> Thanks for the explanation. Should I file a new bug regarding this? Only
>> libstdc++ fails below:
>>
> You can, if you feel strongly about it. I presonally would not put that as
> high priority issue, unless they are reports
> of actual impact on non-synthetic examples.
>
>> #include <functional>
>>
>> int main() {
>>   struct S {
>>     void f() { };
>>   };
>>
>>   S s;
>>   volatile auto sr = std::ref(s);
>>   static_assert(!std::is_invocable_v<decltype(&S::f), decltype(sr)>);
>> }
>>
>> https://godbolt.org/z/b5oP75613
>>
>>
>>
>>
>> Hewill Kang <[email protected]> 於 2025年10月25日 週六 上午12:15寫道:
>>
>>> Here are some examples:
>>>
>>> struct S {
>>>   void f()  const;
>>> };
>>>
>>> int main() {
>>>   S s;
>>>   volatile auto sr = std::ref(s);
>>>   std::function_ref<void() const> fr(std::nontype<&S::f>, sr);
>>>   fr();
>>> }
>>>
>>>
>>> Hewill Kang <[email protected]> 於 2025年10月25日 週六 上午12:10寫道:
>>>
>>>> `volatile std::reference_wrapper<S>` will cause `_Ur __uref = __ref;`
>>>> hard error. But I'm not sure we really need to care about it.
>>>>
>>>> Tomasz Kamiński <[email protected]> 於 2025年10月24日 週五 下午11:33寫道:
>>>>
>>>>> 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