According to your explanation, I have noticed that the following code should be rejected because the constraints are not satisfied (i.e., is-invocable-using should be false).
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 >>> >>>
