On Wed, 28 May 2025, Tomasz Kaminski wrote:

> 
> 
> On Wed, May 28, 2025 at 4:53 PM Patrick Palka <ppa...@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 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
>       >
>       >
> 
> 
> 

Reply via email to