[PATCH] libstdc++: Pass small trivial types by value in polymorphic wrappers
Tomasz Kaminski
tkaminsk@redhat.com
Thu May 29 06:40:44 GMT 2025
On Wed, May 28, 2025 at 5:25 PM Patrick Palka <ppalka@redhat.com> wrote:
> On Wed, 28 May 2025, Tomasz Kaminski wrote:
>
> >
> >
> > On Wed, May 28, 2025 at 4:53 PM Patrick Palka <ppalka@redhat.com> wrote:
> > On Wed, 28 May 2025, Tomasz Kamiński wrote:
> >
> > > This patch adjust the passing of parameters for the
> move_only_function,
> > > copyable_function and function_ref. For types that are declared
> as being passed
> > > by value in signature template argument, the are passed by value
> to the invoker,
> >
> > they
> >
> > > when they are small (at most two pointers), trivially move
> constructible and
> > > trivially destructible. The later guarantees that passing them
> by value has not
> >
> > latter
> >
> > > user visible side effects.
> > >
> > > In particular, this extents the set of types forwarded by value,
> that was
> >
> > extends
> >
> > > previously limited to scalars, to also include specializations
> of std::span and
> > > std::string_view, and similar standard and program defined-types.
> > >
> > > Checking the suitability of the parameter types requires the
> types to be complete.
> > > As consequence implementation imposes requirements on
> instantiation of
>
> I think you want "As a consequence, the"
>
> > > move_only_function and copyable_function. To avoid producing the
> errors from
> > > the implementation details, and static_assertion was added to
> partial
>
> a static assertion
>
> > > specializations of copyable_function, move_only_function and
> function_ref.
> > > The static assertion uses existing __is_complete_or_unbounded,
> as arrays type
> > > parameters are automatically decayed in function type.
> > >
> > > Standard already specifies in [res.on.functions] p2.5 that
> instantiating these
> > > partial specialization with incomplete types leads to undefined
> behavior.
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > > * include/bits/funcwrap.h (__polyfunc::__pass_by_rref):
> Define.
> > > (__polyfunc::__param_t): Update to use __pass_by_rref.
> > > * include/bits/cpyfunc_impl.h:: Assert that are parameters
> type
> > > are complete.
> > > * include/bits/funcref_impl.h: Likewise.
> > > * include/bits/mofunc_impl.h: Likewise.
> > > * testsuite/20_util/copyable_function/call.cc: New test.
> > > * testsuite/20_util/function_ref/call.cc: New test.
> > > * testsuite/20_util/move_only_function/call.cc: New test.
> > > * testsuite/20_util/copyable_function/conv.cc: New test.
> > > * testsuite/20_util/function_ref/conv.cc: New test.
> > > * testsuite/20_util/move_only_function/conv.cc: New test.
> > > * testsuite/20_util/copyable_function/incomplete_neg.cc:
> New test.
> > > * testsuite/20_util/function_ref/incomplete_neg.cc: New
> test.
> > > * testsuite/20_util/move_only_function/incomplete_neg.cc:
> New test.
> > > ---
> > > Tested on x86_54-linux. OK for trunk?
> > >
> > > libstdc++-v3/include/bits/cpyfunc_impl.h | 4 +++
> > > libstdc++-v3/include/bits/funcref_impl.h | 4 +++
> > > libstdc++-v3/include/bits/funcwrap.h | 18 +++++++++-
> > > libstdc++-v3/include/bits/mofunc_impl.h | 4 +++
> > > .../20_util/copyable_function/call.cc | 7 ++--
> > > .../20_util/copyable_function/conv.cc | 35
> +++++++++++++++++++
> > > .../copyable_function/incomplete_neg.cc | 18 ++++++++++
> > > .../testsuite/20_util/function_ref/call.cc | 10 +++---
> > > .../testsuite/20_util/function_ref/conv.cc | 34
> ++++++++++++++++++
> > > .../20_util/function_ref/incomplete_neg.cc | 18 ++++++++++
> > > .../20_util/move_only_function/call.cc | 7 ++--
> > > .../20_util/move_only_function/conv.cc | 35
> +++++++++++++++++++
> > > .../move_only_function/incomplete_neg.cc | 18 ++++++++++
> > > 13 files changed, 200 insertions(+), 12 deletions(-)
> > > create mode 100644
> libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc
> > > create mode 100644
> libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc
> > > create mode 100644
> libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc
> > >
> > > diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h
> b/libstdc++-v3/include/bits/cpyfunc_impl.h
> > > index bc44cd3e313..f1918ddf87a 100644
> > > --- a/libstdc++-v3/include/bits/cpyfunc_impl.h
> > > +++ b/libstdc++-v3/include/bits/cpyfunc_impl.h
> > > @@ -64,6 +64,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > _GLIBCXX_MOF_REF noexcept(_Noex)>
> > > : __polyfunc::_Cpy_base
> > > {
> > > + static_assert(
> > > +
> (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...),
> > > + "each parameter type must be a complete class");
> > > +
> > > using _Base = __polyfunc::_Cpy_base;
> > > using _Invoker = __polyfunc::_Invoker<_Noex, _Res,
> _ArgTypes...>;
> > > using _Signature = _Invoker::_Signature;
> > > diff --git a/libstdc++-v3/include/bits/funcref_impl.h
> b/libstdc++-v3/include/bits/funcref_impl.h
> > > index 1e19866035f..44c992281be 100644
> > > --- a/libstdc++-v3/include/bits/funcref_impl.h
> > > +++ b/libstdc++-v3/include/bits/funcref_impl.h
> > > @@ -68,6 +68,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV
> > > noexcept(_Noex)>
> > > {
> > > + static_assert(
> > > +
> (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...),
> > > + "each parameter type must be a complete class");
> > > +
> > > using _Invoker = __polyfunc::_Invoker<_Noex, _Res,
> _ArgTypes...>;
> > > using _Signature = _Invoker::_Signature;1
> > >
> > > diff --git a/libstdc++-v3/include/bits/funcwrap.h
> b/libstdc++-v3/include/bits/funcwrap.h
> > > index cf261bcd4c8..4fa9e1e84f2 100644
> > > --- a/libstdc++-v3/include/bits/funcwrap.h
> > > +++ b/libstdc++-v3/include/bits/funcwrap.h
> > > @@ -199,7 +199,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > };
> > >
> > > template<typename _Tp>
> > > - using __param_t = __conditional_t<is_scalar_v<_Tp>, _Tp,
> _Tp&&>;
> > > + consteval bool __pass_by_rref()
> >
> > Newline before the function name
> >
> > I find it slightly easier to follow instead define the inverse
> predicate
> > __pass_by_value, which IIUC would look like
> >
> > if constexpr (__is_complete_or_unbounded)
> > return false;
> > else
> > return is_reference_v<_Tp>
> > || is_scalar_v<_Tp>
> > || (sizeof(_Tp) <= 2 * sizeof(void*)
> > && is_trivially_move_constructible_v
> > && is_trivially_destructible_v)
> >
> > This will instantiate sizeof() and is_trivially_blah_v for reference
> and scalars, and I wanted to explicitly
> > avoid doing so. This is especially important for Incomplete&, where
> sizeof(Incomplete&) is same as
> > sizeof(Incomplete) and ill-formed.
> > I created this exposition function, so I can do constexpr chains that
> will avoid doing any of the above.
>
> Ah, makes sense. Then I suppose inverting the predicate wouldn't really
> make things simpler, so never mind.
>
I missed the change to by_value. I will see if I can simplify it, and
avoid some negations.
>
> >
> > I don't feel strongly about it though.
> >
> > > + {
> > > + if constexpr (is_reference_v<_Tp> || is_scalar_v<_Tp>)
> > > + return false;
> > > + else if constexpr
> (!std::__is_complete_or_unbounded(__type_identity<_Tp>()))
> > > + // N.B. we already asserted that types are complete in
> wrappers,
> > > + // avoid triggering additional errors from this function.
> > > + return true;
> > > + else if constexpr (sizeof(_Tp) <= 2 * sizeof(void*))
> > > + return !is_trivially_move_constructible_v<_Tp>
> > > + || !is_trivially_destructible_v<_Tp>;
> >
> > Just curious, why check move_constructible + destructible instead
> of
> > just is_trivially_copy_constructible?
> >
> > When we pass objects around, we perform moves and not copies. And I can
> imagine a class,
> > with a trivial move constructor but non-trivial copy constructor or
> deleted.
> > So, I am checking if ops that are inserted when passing by-value, have
> no user visible side-effects,
> > trivial for sure do not.
> > And for checking is_destructible, I am not sure if standard requires
> that is_trivally_constructible,
> > implies that destructors are trivial.
>
> Thanks, LGTM besides a few more typos in the commit message noted above.
>
> >
> > > + else
> > > + return true;
> > > + }
> > > +
> > > + template<typename _Tp>
> > > + using __param_t = __conditional_t<__pass_by_rref<_Tp>(),
> _Tp&&, _Tp>;
> > >
> > > template<bool _Noex, typename _Ret, typename... _Args>
> > > using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>,
> __param_t<_Args>...>;
> > > diff --git a/libstdc++-v3/include/bits/mofunc_impl.h
> b/libstdc++-v3/include/bits/mofunc_impl.h
> > > index 1ceb910e815..468e6855fc6 100644
> > > --- a/libstdc++-v3/include/bits/mofunc_impl.h
> > > +++ b/libstdc++-v3/include/bits/mofunc_impl.h
> > > @@ -64,6 +64,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > _GLIBCXX_MOF_REF noexcept(_Noex)>
> > > : __polyfunc::_Mo_base
> > > {
> > > + static_assert(
> > > +
> (std::__is_complete_or_unbounded(__type_identity<_ArgTypes>()) && ...),
> > > + "each parameter type must be a complete class");
> > > +
> > > using _Base = __polyfunc::_Mo_base;
> > > using _Invoker = __polyfunc::_Invoker<_Noex, _Res,
> _ArgTypes...>;
> > > using _Signature = _Invoker::_Signature;
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
> b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
> > > index cf997577f62..0ac5348f2fe 100644
> > > --- a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc
> > > @@ -204,13 +204,14 @@ test05()
> > > }
> > >
> > > struct Incomplete;
> > > +enum CompleteEnum : int;
> > >
> > > void
> > > test_params()
> > > {
> > > - std::copyable_function<void(Incomplete)> f1;
> > > - std::copyable_function<void(Incomplete&)> f2;
> > > - std::copyable_function<void(Incomplete&&)> f3;
> > > + std::copyable_function<void(Incomplete&)> f1;
> > > + std::copyable_function<void(Incomplete&&)> f2;
> > > + std::copyable_function<void(CompleteEnum)> f4;
> > > }
> > >
> > > int main()
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> > > index e678e166172..11c839b6932 100644
> > > --- a/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc
> > > @@ -2,6 +2,7 @@
> > > // { dg-require-effective-target hosted }
> > >
> > > #include <functional>
> > > +#include <string_view>
> > > #include <testsuite_hooks.h>
> > >
> > > using std::copyable_function;
> > > @@ -15,6 +16,21 @@ static_assert(
> !std::is_constructible_v<std::copyable_function<void()&>,
> > > static_assert(
> !std::is_constructible_v<std::copyable_function<void() const>,
> > >
> std::copyable_function<void()>> );
> > >
> > > +using FuncType = int(int);
> > > +
> > > +// Top level const qualifiers are ignored and decay is
> performed in parameters
> > > +// of function_types.
> > > +static_assert( std::is_same_v<std::copyable_function<void(int
> const)>,
> > > + std::copyable_function<void(int)>> );
> > > +static_assert(
> std::is_same_v<std::copyable_function<void(int[2])>,
> > > + std::copyable_function<void(int*)>>);
> > > +static_assert(
> std::is_same_v<std::copyable_function<void(int[])>,
> > > + std::copyable_function<void(int*)>>);
> > > +static_assert( std::is_same_v<std::copyable_function<void(int
> const[5])>,
> > > + std::copyable_function<void(int
> const*)>>);
> > > +static_assert(
> std::is_same_v<std::copyable_function<void(FuncType)>,
> > > +
> std::copyable_function<void(FuncType*)>>);
> > > +
> > > // Non-trivial args, guarantess that type is not passed by copy
> > > struct CountedArg
> > > {
> > > @@ -240,6 +256,24 @@ test06()
> > > VERIFY( f2(c) == 2 );
> > > }
> > >
> > > +void
> > > +test07()
> > > +{
> > > + // Scalar types and small trivially move constructible types
> are passed
> > > + // by value to invoker. So int&& signature is not compatible
> for such types.
> > > + auto fi = [](CountedArg const& arg, int) noexcept { return
> arg.counter; };
> > > + std::copyable_function<int(CountedArg, int) const noexcept>
> ci1(fi);
> > > + VERIFY( ci1(c, 0) == 1 );
> > > + std::copyable_function<int(CountedArg, int&&) const noexcept>
> ci2(ci1);
> > > + VERIFY( ci2(c, 0) == 2 );
> > > +
> > > + auto fs = [](CountedArg const& arg, std::string_view)
> noexcept { return arg.counter; };
> > > + std::copyable_function<int(CountedArg, std::string_view)
> const noexcept> cs1(fs);
> > > + VERIFY( cs1(c, "") == 1 );
> > > + std::copyable_function<int(CountedArg, std::string_view&&)
> const noexcept> cs2(cs1);
> > > + VERIFY( cs2(c, "") == 2 );
> > > +}
> > > +
> > > int main()
> > > {
> > > test01();
> > > @@ -248,4 +282,5 @@ int main()
> > > test04();
> > > test05();
> > > test06();
> > > + test07();
> > > }
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc
> b/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc
> > > new file mode 100644
> > > index 00000000000..21ddde0d2a1
> > > --- /dev/null
> > > +++
> b/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc
> > > @@ -0,0 +1,18 @@
> > > +// { dg-do compile { target c++26 } }
> > > +
> > > +#include <functional>
> > > +
> > > +struct IncompleteClass;
> > > +
> > > +using T1 =
> std::copyable_function<int(IncompleteClass)>::result_type; // { dg-error
> "here" }
> > > +using T2 = std::copyable_function<int(int,
> IncompleteClass)>::result_type; // { dg-error "here" }
> > > +
> > > +enum Enum {
> > > + x = [] {
> > > + // Enum enumeration is incomplete here
> > > + using T3 = std::copyable_function<int(Enum)>::result_type;
> // { dg-error "here" }
> > > + return T3(1);
> > > + }()
> > > +};
> > > +
> > > +// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> > > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> > > index a91c6b4abb9..23253c33ee3 100644
> > > --- a/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
> > > @@ -164,16 +164,16 @@ test05()
> > > }
> > >
> > > struct Incomplete;
> > > +enum CompleteEnum : int;
> > >
> > > void
> > > test_params()
> > > {
> > > auto f = [](auto&&) {};
> > > - // There is discussion if this is supported.
> > > - // std::function_ref<void(Incomplete)> f1(f);
> > > - std::function_ref<void(Incomplete&)> f2(f);
> > > - // See PR120259, this should be supported.
> > > - // std::function_ref<void(Incomplete&&)> f3(f);
> > > + std::function_ref<void(Incomplete&)> f1(f);
> > > + // See PR libstdc++/120259, this should be supported.
> > > + // std::function_ref<void(Incomplete&&)> f2(f);
> > > + std::function_ref<void(CompleteEnum)> f3(f);
> > > }
> > >
> > > int main()
> > > diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> > > index 7dc7b8ceac0..7606d265f98 100644
> > > --- a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
> > > @@ -2,6 +2,7 @@
> > > // { dg-require-effective-target hosted }
> > >
> > > #include <functional>
> > > +#include <string_view>
> > > #include <testsuite_hooks.h>
> > >
> > > using std::function_ref;
> > > @@ -20,6 +21,21 @@ struct CountedArg
> > > };
> > > CountedArg const c;
> > >
> > > +using FuncType = int(int);
> > > +
> > > +// Top level const qualifiers are ignored in function types,
> and decay
> > > +// is performed.
> > > +static_assert( std::is_same_v<std::function_ref<void(int
> const)>,
> > > + std::function_ref<void(int)>> );
> > > +static_assert( std::is_same_v<std::function_ref<void(int[2])>,
> > > + std::function_ref<void(int*)>>);
> > > +static_assert( std::is_same_v<std::function_ref<void(int[])>,
> > > + std::function_ref<void(int*)>>);
> > > +static_assert( std::is_same_v<std::function_ref<void(int
> const[5])>,
> > > + std::function_ref<void(int
> const*)>>);
> > > +static_assert( std::is_same_v<std::function_ref<void(FuncType)>,
> > > + std::function_ref<void(FuncType*)>>);
> > > +
> > > // The C++26 [func.wrap.general] p2 does not currently cover
> funciton_ref,
> > > // so we make extra copies of arguments.
> > >
> > > @@ -244,6 +260,23 @@ test08()
> > > return true;
> > > };
> > >
> > > +void
> > > +test09()
> > > +{
> > > + // Scalar types and small trivially move constructible types
> are passed
> > > + // by value to invoker. So int&& signature is not compatible
> for such types.
> > > + auto fi = [](CountedArg const& arg, int) noexcept { return
> arg.counter; };
> > > + std::function_ref<int(CountedArg, int) const noexcept>
> ri1(fi);
> > > + VERIFY( ri1(c, 0) == 1 );
> > > + std::function_ref<int(CountedArg, int&&) const noexcept>
> ri2(ri1);
> > > + VERIFY( ri2(c, 0) == 2 );
> > > +
> > > + auto fs = [](CountedArg const& arg, std::string_view)
> noexcept { return arg.counter; };
> > > + std::function_ref<int(CountedArg, std::string_view) const
> noexcept> rs1(fs);
> > > + VERIFY( rs1(c, "") == 1 );
> > > + std::function_ref<int(CountedArg, std::string_view&&) const
> noexcept> rs2(rs1);
> > > + VERIFY( rs2(c, "") == 2 );
> > > +}
> > >
> > > int main()
> > > {
> > > @@ -254,6 +287,7 @@ int main()
> > > test05();
> > > test06();
> > > test07();
> > > + test09();
> > >
> > > static_assert( test08() );
> > > }
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc
> > > new file mode 100644
> > > index 00000000000..c8db1eee42c
> > > --- /dev/null
> > > +++
> b/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc
> > > @@ -0,0 +1,18 @@
> > > +// { dg-do compile { target c++26 } }
> > > +
> > > +#include <functional>
> > > +
> > > +struct IncompleteClass;
> > > +
> > > +int a1 = alignof(std::function_ref<int(IncompleteClass)>); // {
> dg-error "here" }
> > > +int a2 = alignof(std::function_ref<int(int, IncompleteClass)>);
> // { dg-error "here" }
> > > +
> > > +enum Enum {
> > > + x = [] {
> > > + // Enum enumeration is incomplete here
> > > + int a3 = alignof(std::function_ref<int(Enum)>); // {
> dg-error "here" }
> > > + return 1;
> > > + }()
> > > +};
> > > +
> > > +// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc
> b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc
> > > index 217de374763..72c8118d716 100644
> > > --- a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc
> > > @@ -204,13 +204,14 @@ test05()
> > > }
> > >
> > > struct Incomplete;
> > > +enum CompleteEnum : int;
> > >
> > > void
> > > test_params()
> > > {
> > > - std::move_only_function<void(Incomplete)> f1;
> > > - std::move_only_function<void(Incomplete&)> f2;
> > > - std::move_only_function<void(Incomplete&&)> f3;
> > > + std::move_only_function<void(Incomplete&)> f1;
> > > + std::move_only_function<void(Incomplete&&)> f2;
> > > + std::move_only_function<void(CompleteEnum)> f4;
> > > }
> > >
> > > int main()
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc
> b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc
> > > index 3da5e9e90a3..ef8bb37b232 100644
> > > --- a/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc
> > > +++ b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc
> > > @@ -2,6 +2,7 @@
> > > // { dg-require-effective-target hosted }
> > >
> > > #include <functional>
> > > +#include <string_view>
> > > #include <testsuite_hooks.h>
> > >
> > > using std::move_only_function;
> > > @@ -15,6 +16,21 @@ static_assert(
> !std::is_constructible_v<std::move_only_function<void()&>,
> > > static_assert(
> !std::is_constructible_v<std::move_only_function<void() const>,
> > >
> std::move_only_function<void()>> );
> > >
> > > +using FuncType = int(int);
> > > +
> > > +// Top level const qualifiers are ignored in function types,
> and decay
> > > +// is performed.
> > > +static_assert( std::is_same_v<std::move_only_function<void(int
> const)>,
> > > + std::move_only_function<void(int)>>
> );
> > > +static_assert(
> std::is_same_v<std::move_only_function<void(int[2])>,
> > > +
> std::move_only_function<void(int*)>>);
> > > +static_assert(
> std::is_same_v<std::move_only_function<void(int[])>,
> > > +
> std::move_only_function<void(int*)>>);
> > > +static_assert( std::is_same_v<std::move_only_function<void(int
> const[5])>,
> > > + std::move_only_function<void(int
> const*)>>);
> > > +static_assert(
> std::is_same_v<std::move_only_function<void(FuncType)>,
> > > +
> std::move_only_function<void(FuncType*)>>);
> > > +
> > > // Non-trivial args, guarantess that type is not passed by copy
> > > struct CountedArg
> > > {
> > > @@ -177,6 +193,24 @@ test06()
> > > VERIFY( m1(c) == 2 );
> > > }
> > >
> > > +void
> > > +test07()
> > > +{
> > > + // Scalar types and small trivially move constructible types
> are passed
> > > + // by value to invoker. So int&& signature is not compatible
> for such types.
> > > + auto fi = [](CountedArg const& arg, int) noexcept { return
> arg.counter; };
> > > + std::move_only_function<int(CountedArg, int) const noexcept>
> mi1(fi);
> > > + VERIFY( mi1(c, 0) == 1 );
> > > + std::move_only_function<int(CountedArg, int&&) const
> noexcept> mi2(std::move(mi1));
> > > + VERIFY( mi2(c, 0) == 2 );
> > > +
> > > + auto fs = [](CountedArg const& arg, std::string_view)
> noexcept { return arg.counter; };
> > > + std::move_only_function<int(CountedArg, std::string_view)
> const noexcept> ms1(fs);
> > > + VERIFY( ms1(c, "") == 1 );
> > > + std::move_only_function<int(CountedArg, std::string_view&&)
> const noexcept> ms2(std::move(ms1));
> > > + VERIFY( ms2(c, "") == 2 );
> > > +}
> > > +
> > > int main()
> > > {
> > > test01();
> > > @@ -185,4 +219,5 @@ int main()
> > > test04();
> > > test05();
> > > test06();
> > > + test07();
> > > }
> > > diff --git
> a/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc
> b/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc
> > > new file mode 100644
> > > index 00000000000..d025c473e28
> > > --- /dev/null
> > > +++
> b/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc
> > > @@ -0,0 +1,18 @@
> > > +// { dg-do compile { target c++23 } }
> > > +
> > > +#include <functional>
> > > +
> > > +struct IncompleteClass;
> > > +
> > > +using T1 =
> std::move_only_function<int(IncompleteClass)>::result_type; // { dg-error
> "here" }
> > > +using T2 = std::move_only_function<int(int,
> IncompleteClass)>::result_type; // { dg-error "here" }
> > > +
> > > +enum Enum {
> > > + x = [] {
> > > + // Enum enumeration is incomplete here
> > > + using T3 = std::move_only_function<int(Enum)>::result_type;
> // { dg-error "here" }
> > > + return T3(1);
> > > + }()
> > > +};
> > > +
> > > +// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> > > --
> > > 2.49.0
> > >
> > >
> >
> >
> >
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250529/7c2583dc/attachment-0001.htm>
More information about the Libstdc++
mailing list