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