This patch uses the additional provisions from LWG 4264, to avoid double
indirection when function_ref is constructed from function_ref with
compatible signature.
The details of compatible signatures follows the move_only_function
as described in r16-617-g708d40ff109c6e49d02b684a368571722a160af8.
However, due the non-owning nature of function_ref, it operator()
is always const-quaulified, that means that we can constructor a
function_ref<Ret(Args...) const> from function_ref<Ret(Args...)>,
even if the underyling target will be mutated.
The implementations moves the _M_ptrs members to newly defined base
class __polyfunc::_Ref_base. This allows us to reuse existing __base_of
and __invoker_of accessor in the implementation (after befriending them).
The accessors functions are also now marked as constexpr.
Furthermore we move _M_init function there, making it instantiations
independent from callback signature.
PR libstdc++/119126
libstdc++-v3/ChangeLog:
* include/bits/cpyfunc_impl.h: (__polyfunc::__invoker_of)
(__polyfunc::_base_of): Mark as constexpr.
* include/bits/funcref_impl.h (std::function_ref): Add base class
of type__polyfunc::_Ref_base. Befriend __invoker_of, __base_of,
__is_invoker_convertible.
(function_ref::_Base): Define.
(function_ref::_M_init, function_ref::_M_ptrs): Move to base class.
(function_ref::function_ref(_Fn&&)): Handle specializations of
function_ref.
(function_ref::function_ref): Init base class before _M_invoke
consistently.
* include/bits/funcwrap.h : (__polyfunc::__invoker_of)
(__polyfunc::_base_of): Mark as constexpr.
(__polyfunc::__is_function_ref_v, __polyfunc::_Ref_base): Define.
* include/bits/mofunc_impl.h: (__polyfunc::__invoker_of)
(__polyfunc::_base_of): Mark as constexpr.
* testsuite/20_util/function_ref/conv.cc: Updated test to illustrate
that double indirection is avoided. Add constexpr tests.
---
libstdc++-v3/include/bits/cpyfunc_impl.h | 4 +-
libstdc++-v3/include/bits/funcref_impl.h | 48 ++++++++++++-------
libstdc++-v3/include/bits/funcwrap.h | 26 +++++++++-
libstdc++-v3/include/bits/mofunc_impl.h | 4 +-
.../testsuite/20_util/function_ref/conv.cc | 27 ++++++++---
5 files changed, 80 insertions(+), 29 deletions(-)
diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h
b/libstdc++-v3/include/bits/cpyfunc_impl.h
index bc44cd3e313..7ba74ed8a8d 100644
--- a/libstdc++-v3/include/bits/cpyfunc_impl.h
+++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
@@ -248,11 +248,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename _Invoker::__storage_func_t _M_invoke = nullptr;
template<typename _Func>
- friend auto&
+ friend constexpr auto&
__polyfunc::__invoker_of(_Func&) noexcept;
template<typename _Func>
- friend auto&
+ friend constexpr auto&
__polyfunc::__base_of(_Func&) noexcept;
template<typename _Dst, typename _Src>
diff --git a/libstdc++-v3/include/bits/funcref_impl.h
b/libstdc++-v3/include/bits/funcref_impl.h
index 1e19866035f..f3ec765b953 100644
--- a/libstdc++-v3/include/bits/funcref_impl.h
+++ b/libstdc++-v3/include/bits/funcref_impl.h
@@ -67,7 +67,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Res, typename... _ArgTypes, bool _Noex>
class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
noexcept(_Noex)>
+ : __polyfunc::_Ref_base
{
+ using _Base = __polyfunc::_Ref_base;
using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>;
using _Signature = _Invoker::_Signature;
@@ -85,8 +87,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
function_ref(_Fn* __fn) noexcept
{
__glibcxx_assert(__fn != nullptr);
- _M_invoke = _Invoker::template _S_ptrs<_Fn*>();
_M_init(__fn);
+ _M_invoke = _Invoker::template _S_ptrs<_Fn*>();
}
/// Target and bound object is object referenced by parameter.
@@ -102,8 +104,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr
function_ref(_Fn&& __f) noexcept
{
- _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
- _M_init(std::addressof(__f));
+ using _Vd = remove_cv_t<_Vt>;
+ if constexpr (__is_function_ref_v<_Vd>
+ && __polyfunc::__is_invoker_convertible<_Vd, function_ref>())
+ {
+ _Base::operator=(__polyfunc::__base_of(__f));
+ _M_invoke = __polyfunc::__invoker_of(__f);
+ }
+ else
+ {
+ using _Tr = _Vt _GLIBCXX_MOF_CV&;
+ _M_init(std::addressof(__f));
+ _M_invoke = _Invoker::template _S_ptrs<_Tr>();
+ }
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
@@ -118,8 +131,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
static_assert(__fn != nullptr);
- _M_invoke = &_Invoker::template _S_nttp<__fn>;
_M_ptrs._M_obj = nullptr;
+ _M_invoke = &_Invoker::template _S_nttp<__fn>;
}
/// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)).
@@ -135,13 +148,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_assert(__fn != nullptr);
using _Tr = _Td _GLIBCXX_MOF_CV&;
+ _M_init(std::addressof(__ref));
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>;
else
_M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>;
- _M_init(std::addressof(__ref));
}
/// Target object is equivalent to std::bind_front<_fn>(__ptr).
@@ -157,8 +170,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if constexpr (is_member_pointer_v<_Fn>)
__glibcxx_assert(__ptr != nullptr);
- _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td
_GLIBCXX_MOF_CV>;
_M_init(__ptr);
+ _M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td
_GLIBCXX_MOF_CV>;
}
template<typename _Tp>
@@ -178,18 +191,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return _M_invoke(_M_ptrs, std::forward<_ArgTypes>(__args)...); }
private:
- template<typename _Tp>
- constexpr void
- _M_init(_Tp* __ptr) noexcept
- {
- if constexpr (is_function_v<_Tp>)
- _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr);
- else
- _M_ptrs._M_obj = __ptr;
- }
-
typename _Invoker::__ptrs_func_t _M_invoke;
- __polyfunc::_Ptrs _M_ptrs;
+
+ template<typename _Func>
+ friend constexpr auto&
+ __polyfunc::__invoker_of(_Func&) noexcept;
+
+ template<typename _Func>
+ friend constexpr auto&
+ __polyfunc::__base_of(_Func&) noexcept;
+
+ template<typename _Dst, typename _Src>
+ friend consteval bool
+ __polyfunc::__is_invoker_convertible() noexcept;
};
#undef _GLIBCXX_MOF_CV
diff --git a/libstdc++-v3/include/bits/funcwrap.h
b/libstdc++-v3/include/bits/funcwrap.h
index cf261bcd4c8..1c1ba637689 100644
--- a/libstdc++-v3/include/bits/funcwrap.h
+++ b/libstdc++-v3/include/bits/funcwrap.h
@@ -205,12 +205,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>,
__param_t<_Args>...>;
template<typename _Func>
- auto&
+ constexpr auto&
__invoker_of(_Func& __f) noexcept
{ return __f._M_invoke; }
template<typename _Func>
- auto&
+ constexpr auto&
__base_of(_Func& __f) noexcept
{ return static_cast<__like_t<_Func&, typename _Func::_Base>>(__f); }
@@ -518,6 +518,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// @cond undocumented
namespace __polyfunc
{
+ struct _Ref_base
+ {
+ template<typename _Tp>
+ constexpr void
+ _M_init(_Tp* __ptr) noexcept
+ {
+ if constexpr (is_function_v<_Tp>)
+ _M_ptrs._M_func = reinterpret_cast<void(*)()>(__ptr);
+ else
+ _M_ptrs._M_obj = __ptr;
+ }
+
+ _Ptrs _M_ptrs;
+ };
+
template<typename _Sig>
struct __skip_first_arg;
@@ -556,6 +571,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
-> function_ref<
remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn,
_Tp&>())>>;
+ /// @cond undocumented
+ template<typename _Tp>
+ constexpr bool __is_function_ref_v = false;
+ template<typename _Tp>
+ constexpr bool __is_function_ref_v<function_ref<_Tp>> = true;
+ /// @endcond
+
#endif // __glibcxx_function_ref
_GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/bits/mofunc_impl.h
b/libstdc++-v3/include/bits/mofunc_impl.h
index 1ceb910e815..017678d1d51 100644
--- a/libstdc++-v3/include/bits/mofunc_impl.h
+++ b/libstdc++-v3/include/bits/mofunc_impl.h
@@ -232,11 +232,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename _Invoker::__storage_func_t _M_invoke = nullptr;
template<typename _Func>
- friend auto&
+ friend constexpr auto&
__polyfunc::__invoker_of(_Func&) noexcept;
template<typename _Func>
- friend auto&
+ friend constexpr auto&
__polyfunc::__base_of(_Func&) noexcept;
template<typename _Dst, typename _Src>
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
index 49ff0a78829..d4bf15c03a9 100644
--- a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
@@ -20,8 +20,8 @@ struct CountedArg
};
CountedArg const c;
-// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref,
-// so we make extra copies of arguments.
+// LWG 4264 modify [func.wrap.general] p2 to also cover funciton_ref,
+// so we make avoid extra copies in some situations.
void
test01()
@@ -38,14 +38,14 @@ test01()
VERIFY( r2c(c) == 2 );
std::function_ref<int(CountedArg) const> r3r(r1);
- VERIFY( r3r(c) == 2 );
+ VERIFY( r3r(c) == 1 );
std::function_ref<int(CountedArg) const> r3m(m1);
VERIFY( r3m(c) == 2 );
std::function_ref<int(CountedArg) const> r3c(c1);
VERIFY( r3c(c) == 2 );
std::function_ref<int(CountedArg)> r4r(r1);
- VERIFY( r4r(c) == 2 );
+ VERIFY( r4r(c) == 1 );
std::function_ref<int(CountedArg)> r4m(m1);
VERIFY( r4m(c) == 2 );
std::function_ref<int(CountedArg)> r4c(c1);
@@ -94,7 +94,7 @@ test03()
// Call const overload as std::function_ref<int(CountedArg) const>
// inside std::function_ref<int(CountedArg)> would do.
std::function_ref<int(CountedArg)> r2(r1);
- VERIFY( r2(c) == 1002 );
+ VERIFY( r2(c) == 1001 );
std::move_only_function<int(CountedArg)> m2(r1);
VERIFY( m2(c) == 1002 );
@@ -103,7 +103,7 @@ test03()
std::function_ref<int(CountedArg)> r3(f);
VERIFY( r3(c) == 1 );
std::function_ref<int(CountedArg) const> r4(r3);
- VERIFY( r4(c) == 2 );
+ VERIFY( r4(c) == 1 );
std::move_only_function<int(CountedArg) const> m4(r3);
VERIFY( m4(c) == 2 );
}
@@ -142,6 +142,18 @@ test05()
VERIFY( f2(c) == 2 );
}
+constexpr bool
+test07()
+{
+ auto f = [](int x) noexcept { return x; };
+ std::function_ref<int(int) const noexcept> rf(f);
+
+ std::function_ref<int(int) const noexcept> rr1(rf);
+ std::function_ref<int(int)> rr2(rf);
+ std::function_ref<int(long)> rr3(rf);
+ return true;
+};
+
int main()
{
test01();
@@ -149,4 +161,7 @@ int main()
test03();
test04();
test05();
+ test07();
+
+ static_assert( test07() );
}
--
2.49.0