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