I just realized that unwrapping reference wrapper is observable, and not
permitted by standard,
for example this should pass
struct S { int x; };
S s1, s2;
std:::refernce_wrapper<S1> rw(s1);
std::function_ref<int&()> fr(std::nontype<&S::x>, rw);
assert( &fr() == &s1.x );
rw = std::ref(s2); // rebind
assert( &f1() == &s2.x ); // the rebind must be observed.I will post a new patch, with the above fixed. On Sat, Oct 25, 2025 at 7:01 AM Hewill Kang <[email protected]> wrote: > 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 >>>>>> >>>>>>
