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