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
>>>>
>>>>