C++ Logo

sg14

Advanced search

Re: [isocpp-sg14] Introducing `Embedded Function` - A Heap-Free Function Wrapper

From: Scott Dixon <scottd.thirtytwo_at_[hidden]>
Date: Mon, 17 Aug 2026 11:47:38 -0700
Did we not already consider
https://github.com/WG21-SG14/SG14/blob/master/Docs/Proposals/NonAllocatingStandardFunction.pdf
? I'm not sure where that paper ended up or if it's still a proposal?

On Mon, Aug 17, 2026 at 8:19 AM Arthur O'Dwyer via SG14 <
sg14_at_[hidden]> wrote:

> On Mon, Aug 17, 2026 at 10:56 AM Kim J. Smith via SG14 <
> sg14_at_[hidden]> wrote:
>
>> > `F` satisfies the current `is_empty_trivial` test, but constructing an
>> owning wrapper from `f` would normally construct a target object and
>> therefore invoke the copy constructor. Eliminating the object also
>> eliminates that observable effect.
>> >
>> > More generally, an empty object does not imply that its identity or
>> lifetime is unobservable; `operator()` may also use `this`. So I suspect
>> that trying to infer semantic statelessness from representation/type traits
>> is difficult to make generally safe.
>>
>> This is indeed an area where I didn't think things through thoroughly. I
>> used to think `is_empty_trivial` does not indicate stateless only in
>> non-owning semantics (`IsView` == true) and ignored the side effects in the
>> lifetime management in owning semantics.
>>
>> There are basically two categories of side effects:
>> 1. side effects in lifetime management;
>> 2. use of `this` in `operator()`.
>>
>> For the first category, adding `std::is_trivially_move_constructible` and
>> `std::is_trivially_copy_constructible` would be sufficient to eliminate the
>> issue. However, there seems to be no reliable way to eliminate the second
>> category.
>>
>
> You could "eliminate" it by relaxing the guarantees you document for your
> wrapper. Instead of documenting that you always call the stored callable,
> you could document that you call "the stored callable, or a copy of it,"
> and furthermore document that you do *not* make copies *unless* the
> stored callable is trivially copyable (i.e. you guarantee never to make an
> expensive copy).
> There is then no observable difference between
>
> EmptyTrivial t_; // trivially default-constructible, trivially
> copyable, and empty
> void operator()() const { auto copy = t_; copy(); } // what you
> document
>
> and
>
> void operator()() const { EmptyTrivial copy; copy(); } // what you
> actually do
>
> In both cases, `&copy` is documented to be not-necessarily-the-same as
> `&t_`.
>
> Vice versa, a type with a static operator() can't use `this` inside its
> `operator()`, but it could still have a non-trivial default constructor
> and/or copy and/or destructor.
> I wouldn't try to detect static operator(); that seems like it could lead
> to SFINAE-unfriendly errors when the user wasn't expecting it.
> template<class T, class... Args>
> concept StaticCallable = requires {
> T::operator()(std::declval<Args>()...); };
> You might also find pathological types such that `T::operator()(args...)`
> is well-formed but `t(args...)` calls a different overload.
>
> (Yes, you can also find pathological types that are trivially copyable but
> `auto copy = t_;` calls a different overload! But that's a long-standing
> and "well-known" pathology: If someone gives you a type like that, I feel
> perfectly justified in saying "don't do that then." Playing games with
> static `operator()` is obviously newer and less well-studied.)
>
> –Arthur
> _______________________________________________
> SG14 mailing list
> SG14_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/sg14
>

Received on 2026-08-17 18:47:56