Date: Sat, 10 Jan 2026 21:16:38 +0100
On 1/10/26 21:02, Jody Hagins via Std-Proposals wrote:
> But that is my perceived solution to the real thing I want, and that is to have more latitude in what is an implicit lifetime type.
Why? I think what you really want is a different precondition
for start_lifetime_as. As far as I can see from your examples,
it's totally fine to have an explicit operation that creates
those objects in your local abstract machine; you need to know
there's (say) a std::atomic at that location anyway.
Whether a relaxation of the implicit lifetime definition is the
right approach to achieve that remains to be seen; at least
conceptually, an explicit operation giving a particular type
seems easier on all parties involved than the angelic object
creation that other incarnations of implicit lifetime objects
imply.
Jens
> Currently, the rule is clear: at least one constructor must be trivial.
>
> Currently, the language only allows the default, copy, and move constructors to be trivial.
>
> So, I didn't think I had a chance of redefining what an implicit lifetime type meant, but I did think that I might have a chance at coming up with a way to get past the trivial constructor hurdle.
>
> The actual problem nests, kinda like swiss cheese.
>
> Because, you can have this...
>
> ```c++
> struct Foo
> {
> Bar bar;
> Baz baz;
> };
> static_assert(std::is_implicit_lifetime_type_v<Bar>);
> static_assert(std::is_implicit_lifetime_type_v<Baz>);
> static_assert(not std::is_implicit_lifetime_type_v<Foo>);
> ```
> The only way that Foo will be an implicit lifetime type under the current definition is if both Bar and Baz have at least one trivial constructor IN COMMON. This is another reason I went with the added special ctor because they could all define that one.
>
> Of course, this likely only reinforces your earlier point (assuming I got it right), that what I really need is some change in the implicit lifetime type rules - and maybe this thing too...
>
> Off for a "Christmas" day since my son was in Chicago over Christmas. He and all our kids and granddaughter will be here shortly.
>
> Thanks
>
> Jody
>
>
>
>
>
>> On Jan 10, 2026, at 1:39 PM, Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> wrote:
>>
>> On Sat, Jan 10, 2026 at 9:09 AM Jody Hagins via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>>
>> title: "User-Defined Trivial Constructors"
>> [...]
>> To be an implicit lifetime type, a class must, among other things, have at least one trivial constructor. The only way to explicitly specify a trivial constructor is via `= default`. But you can't only use `= default` user-defined constructors. Hence, no way for anything but the default, copy, and move constructors to be trivial.
>>
>>
>> Here's the actual X in your XY problem. You have a type — let's call it `unique_ptr` — such that you want to be able to do things like this with it:
>>
>> - **Shared memory** — inter-process communication via memory-mapped regions
>> - **Memory-mapped files** — persistent data structures
>> - **Custom allocators** — placement new into raw storage
>> - **`std::start_lifetime_as`** — explicit lifetime management
>>
>>
>> All of these in the real world just require that you have some way of getting the right object-representation into a certain memory location (which of course you do), and then some way of convincing the compiler (or, philosophically, convincing the abstract machine) that there is really an object there already. That is, at runtime the abstract-machine program doesn't need to /create/ an object, it just needs to become /aware/ of the object that's already there.
>>
>> Now, the problem is that you can do the above things only with implicit-lifetime types. Or at least you think you can. Personally I'd like to see the paper contain authoritative chapter-and-verse citations proving that (1) implicit-lifetime-ness is /necessary/ to do these various things without UB, and even more importantly (2) implicit-lifetime-ness is /*sufficient*/ to do these various things without UB.
>>
>> `unique_ptr` is not an implicit-lifetime type. Therefore (pending the above chapter-and-verse citations), on paper, you can't do these things with `unique_ptr`.
>> But of course /physically/ you can do these things with `unique_ptr`.
>> So the paper spec fails to capture the actual behavior of the system. This is a defect in the spec, in one of two places. Either:
>> (A) `unique_ptr` /should/ be recognized as an implicit-lifetime type, or
>> (B) implicit-lifetime-ness /shouldn't/ be considered a prerequisite for doing these various things without UB.
>>
>> Your proposal seems to assume (A), but depending on the outcome of your chapter-and-verse citations, you might need to explain why you don't just pursue (B) instead.
>> Anyway, OK, let's assume (A) for now.
>>
>> Next you explain: "To be an implicit lifetime type, a class must, among other things, have at least one trivial constructor."
>> But `unique_ptr` doesn't have any trivial constructors. /None/ of its special members are trivial. So — given that we're assuming (A) from above — either:
>> (C) `unique_ptr` /should/ gain a trivial constructor (that is not the default, copy, or move constructor, because none of those are physically trivial), or
>> (D) having at least one trivial constructor /shouldn't/ be considered a prerequisite for implicit-lifetime-ness.
>>
>> Your proposal assumes (C), and then proposes a way to achieve (C). But surely (D) is easier, both in terms of wording and in terms of the implementation. (D) just requires altering one sentence <https://eel.is/c++draft/class.prop#8> in the paper spec; vendors don't have to change anything about their implementations at all. (They do have to promise not to implement "optimizations" that would break the current behavior; that is, today they technically have the freedom to exploit UB here, which freedom (D) takes away from them. But they're not /using/ that freedom yet.)
>> Also, because (D) better matches the physical reality, I think it's just /philosophically preferable/ to pursue (D).
>>
>> You write:
>>
>> What we need is a way to have *both*: a safe default constructor *and* a trivial constructor for implicit lifetime purposes.
>>
>>
>> No, what you *need* is a way to have both a safe default constructor and /implicit-lifetime-ness/. I don't think you've motivated the idea that you need a trivial constructor, except that you've assumed not to pursue (D).
>>
>> ----
>>
>> Now, my thoughts on the actual proposal/syntax.
>>
>> explicit Foo(std::trivial_t) = default;
>> The semantics are straightforward:
>> - `= default` on a user-defined constructor means "do nothing"
>> - It is trivial — exactly what `Foo() = default` would do
>>
>> - It is only valid if `Foo() = default` would produce a trivial default constructor
>>
>>
>> This part seems unintuitive to me. I see physically why you need it: your proposal cannot deal with the snippet
>> struct S {
>> std::string m_;
>> explicit S(std::trivial_t) = default; // Defaulted, but also somehow trivial??
>> };
>> without becoming self-contradictory, so, you propose that this snippet should be ill-formed.
>> You get into this self-contradiction because of your syntax-misuse. `=default` does /not/ mean "trivial" (that is, bitwise / object-representation-wise). It means "memberwise" (which may be trivial but also may not). You're trying to use `=default` as a syntactic signal that means "trivial" — but it doesn't /work/ as that signal, because =default` doesn't mean "trivial" to begin with. It means "memberwise."
>> Relevant: P1029 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1029r3.pdf> tried to introduce the syntax `X(X&&) = bitcopies;` as a new kind of signal you can put to indicate that the function just copies the bits. (P1029 totally messed this up, mind you: it annotated the /move constructor/ in order to say something about the /relocation operation/. But it did have the right idea insofar as that you can't use `=default` to mean "copies the bits"; you need a new syntax instead.)
>>
>> Now, getting to the possibly constructive portion of my comments... ;)
>> It seems to me that what you're asking for is at least /very close to/ something that Louis Dionne at libc++ has been wanting for quite a while: libc++ wants a way to mark user-defined constructors as trivial.
>> My own pet example — likely different from Louis' examples — is:
>> struct unique_ptr {
>> int *p_;
>> [[trivial]] explicit unique_ptr(int *p) : p_(p) {}
>> ~~~~
>> };
>> That is, the library author here is telling the compiler to report `is_trivially_constructible_v<unique_ptr<int>, int*>`. This is, /physically/, already true: the way you construct a unique_ptr from an int* is that you copy the bits from the int* into the bits of the unique_ptr. But without the annotation, the compiler doesn't know this, and so for example you get bad codegen for
>> std::uninitialized_copy_n(array_of_intptr, 100, array_of_uniqueptr);
>> (Godbolt <https://godbolt.org/z/fe6ecsEWG>).
>> So, what we could really use is a way to set [[trivial]] on any (single-argument) constructor. Also, it could be useful for debugging purposes to be able to write
>> struct X {
>> int i_ = 0;
>> [[trivial]] X(int i): i_(i) { assert(i_ != 0); }
>> };
>> That is, "I warrant that this constructor is trivial for purposes of optimization and ABI; but also, if you ever do need to generate a call to it, here's what I'd like that call to do."
>> That is, "This type is basically an aggregate, but with a little extra instrumentation."
>>
>> This gets you the ability to do (C) from above (although for your purposes I think you should want (D) instead).
>> It also gets library vendors something they've wanted for a while, something that's generally useful, not just for this specific corner case.
>> It also doesn't abuse the `=default` syntax.
>> It applies also to other functions, such as operator==, operator<=>, and swap.
>> (The attribute would probably want to be rejected with a warning on any function that doesn't have an obvious "bitwise" analogue.)
>>
>> Now, the downsides of the above idea:
>> - it's novel (but so is your syntax proposal: that's a tie).
>> - it relies on the effect of an attribute (which is /controversial/)
>> - it needs to be specced out a lot more than what I wrote above
>>
>> Maybe Louis could confirm or deny that libc++ has applications for "user-defined trivial" functions, and give some better examples than I did.
>>
>> my $.02,
>> –Arthur
>
>
> But that is my perceived solution to the real thing I want, and that is to have more latitude in what is an implicit lifetime type.
Why? I think what you really want is a different precondition
for start_lifetime_as. As far as I can see from your examples,
it's totally fine to have an explicit operation that creates
those objects in your local abstract machine; you need to know
there's (say) a std::atomic at that location anyway.
Whether a relaxation of the implicit lifetime definition is the
right approach to achieve that remains to be seen; at least
conceptually, an explicit operation giving a particular type
seems easier on all parties involved than the angelic object
creation that other incarnations of implicit lifetime objects
imply.
Jens
> Currently, the rule is clear: at least one constructor must be trivial.
>
> Currently, the language only allows the default, copy, and move constructors to be trivial.
>
> So, I didn't think I had a chance of redefining what an implicit lifetime type meant, but I did think that I might have a chance at coming up with a way to get past the trivial constructor hurdle.
>
> The actual problem nests, kinda like swiss cheese.
>
> Because, you can have this...
>
> ```c++
> struct Foo
> {
> Bar bar;
> Baz baz;
> };
> static_assert(std::is_implicit_lifetime_type_v<Bar>);
> static_assert(std::is_implicit_lifetime_type_v<Baz>);
> static_assert(not std::is_implicit_lifetime_type_v<Foo>);
> ```
> The only way that Foo will be an implicit lifetime type under the current definition is if both Bar and Baz have at least one trivial constructor IN COMMON. This is another reason I went with the added special ctor because they could all define that one.
>
> Of course, this likely only reinforces your earlier point (assuming I got it right), that what I really need is some change in the implicit lifetime type rules - and maybe this thing too...
>
> Off for a "Christmas" day since my son was in Chicago over Christmas. He and all our kids and granddaughter will be here shortly.
>
> Thanks
>
> Jody
>
>
>
>
>
>> On Jan 10, 2026, at 1:39 PM, Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> wrote:
>>
>> On Sat, Jan 10, 2026 at 9:09 AM Jody Hagins via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>>
>> title: "User-Defined Trivial Constructors"
>> [...]
>> To be an implicit lifetime type, a class must, among other things, have at least one trivial constructor. The only way to explicitly specify a trivial constructor is via `= default`. But you can't only use `= default` user-defined constructors. Hence, no way for anything but the default, copy, and move constructors to be trivial.
>>
>>
>> Here's the actual X in your XY problem. You have a type — let's call it `unique_ptr` — such that you want to be able to do things like this with it:
>>
>> - **Shared memory** — inter-process communication via memory-mapped regions
>> - **Memory-mapped files** — persistent data structures
>> - **Custom allocators** — placement new into raw storage
>> - **`std::start_lifetime_as`** — explicit lifetime management
>>
>>
>> All of these in the real world just require that you have some way of getting the right object-representation into a certain memory location (which of course you do), and then some way of convincing the compiler (or, philosophically, convincing the abstract machine) that there is really an object there already. That is, at runtime the abstract-machine program doesn't need to /create/ an object, it just needs to become /aware/ of the object that's already there.
>>
>> Now, the problem is that you can do the above things only with implicit-lifetime types. Or at least you think you can. Personally I'd like to see the paper contain authoritative chapter-and-verse citations proving that (1) implicit-lifetime-ness is /necessary/ to do these various things without UB, and even more importantly (2) implicit-lifetime-ness is /*sufficient*/ to do these various things without UB.
>>
>> `unique_ptr` is not an implicit-lifetime type. Therefore (pending the above chapter-and-verse citations), on paper, you can't do these things with `unique_ptr`.
>> But of course /physically/ you can do these things with `unique_ptr`.
>> So the paper spec fails to capture the actual behavior of the system. This is a defect in the spec, in one of two places. Either:
>> (A) `unique_ptr` /should/ be recognized as an implicit-lifetime type, or
>> (B) implicit-lifetime-ness /shouldn't/ be considered a prerequisite for doing these various things without UB.
>>
>> Your proposal seems to assume (A), but depending on the outcome of your chapter-and-verse citations, you might need to explain why you don't just pursue (B) instead.
>> Anyway, OK, let's assume (A) for now.
>>
>> Next you explain: "To be an implicit lifetime type, a class must, among other things, have at least one trivial constructor."
>> But `unique_ptr` doesn't have any trivial constructors. /None/ of its special members are trivial. So — given that we're assuming (A) from above — either:
>> (C) `unique_ptr` /should/ gain a trivial constructor (that is not the default, copy, or move constructor, because none of those are physically trivial), or
>> (D) having at least one trivial constructor /shouldn't/ be considered a prerequisite for implicit-lifetime-ness.
>>
>> Your proposal assumes (C), and then proposes a way to achieve (C). But surely (D) is easier, both in terms of wording and in terms of the implementation. (D) just requires altering one sentence <https://eel.is/c++draft/class.prop#8> in the paper spec; vendors don't have to change anything about their implementations at all. (They do have to promise not to implement "optimizations" that would break the current behavior; that is, today they technically have the freedom to exploit UB here, which freedom (D) takes away from them. But they're not /using/ that freedom yet.)
>> Also, because (D) better matches the physical reality, I think it's just /philosophically preferable/ to pursue (D).
>>
>> You write:
>>
>> What we need is a way to have *both*: a safe default constructor *and* a trivial constructor for implicit lifetime purposes.
>>
>>
>> No, what you *need* is a way to have both a safe default constructor and /implicit-lifetime-ness/. I don't think you've motivated the idea that you need a trivial constructor, except that you've assumed not to pursue (D).
>>
>> ----
>>
>> Now, my thoughts on the actual proposal/syntax.
>>
>> explicit Foo(std::trivial_t) = default;
>> The semantics are straightforward:
>> - `= default` on a user-defined constructor means "do nothing"
>> - It is trivial — exactly what `Foo() = default` would do
>>
>> - It is only valid if `Foo() = default` would produce a trivial default constructor
>>
>>
>> This part seems unintuitive to me. I see physically why you need it: your proposal cannot deal with the snippet
>> struct S {
>> std::string m_;
>> explicit S(std::trivial_t) = default; // Defaulted, but also somehow trivial??
>> };
>> without becoming self-contradictory, so, you propose that this snippet should be ill-formed.
>> You get into this self-contradiction because of your syntax-misuse. `=default` does /not/ mean "trivial" (that is, bitwise / object-representation-wise). It means "memberwise" (which may be trivial but also may not). You're trying to use `=default` as a syntactic signal that means "trivial" — but it doesn't /work/ as that signal, because =default` doesn't mean "trivial" to begin with. It means "memberwise."
>> Relevant: P1029 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1029r3.pdf> tried to introduce the syntax `X(X&&) = bitcopies;` as a new kind of signal you can put to indicate that the function just copies the bits. (P1029 totally messed this up, mind you: it annotated the /move constructor/ in order to say something about the /relocation operation/. But it did have the right idea insofar as that you can't use `=default` to mean "copies the bits"; you need a new syntax instead.)
>>
>> Now, getting to the possibly constructive portion of my comments... ;)
>> It seems to me that what you're asking for is at least /very close to/ something that Louis Dionne at libc++ has been wanting for quite a while: libc++ wants a way to mark user-defined constructors as trivial.
>> My own pet example — likely different from Louis' examples — is:
>> struct unique_ptr {
>> int *p_;
>> [[trivial]] explicit unique_ptr(int *p) : p_(p) {}
>> ~~~~
>> };
>> That is, the library author here is telling the compiler to report `is_trivially_constructible_v<unique_ptr<int>, int*>`. This is, /physically/, already true: the way you construct a unique_ptr from an int* is that you copy the bits from the int* into the bits of the unique_ptr. But without the annotation, the compiler doesn't know this, and so for example you get bad codegen for
>> std::uninitialized_copy_n(array_of_intptr, 100, array_of_uniqueptr);
>> (Godbolt <https://godbolt.org/z/fe6ecsEWG>).
>> So, what we could really use is a way to set [[trivial]] on any (single-argument) constructor. Also, it could be useful for debugging purposes to be able to write
>> struct X {
>> int i_ = 0;
>> [[trivial]] X(int i): i_(i) { assert(i_ != 0); }
>> };
>> That is, "I warrant that this constructor is trivial for purposes of optimization and ABI; but also, if you ever do need to generate a call to it, here's what I'd like that call to do."
>> That is, "This type is basically an aggregate, but with a little extra instrumentation."
>>
>> This gets you the ability to do (C) from above (although for your purposes I think you should want (D) instead).
>> It also gets library vendors something they've wanted for a while, something that's generally useful, not just for this specific corner case.
>> It also doesn't abuse the `=default` syntax.
>> It applies also to other functions, such as operator==, operator<=>, and swap.
>> (The attribute would probably want to be rejected with a warning on any function that doesn't have an obvious "bitwise" analogue.)
>>
>> Now, the downsides of the above idea:
>> - it's novel (but so is your syntax proposal: that's a tie).
>> - it relies on the effect of an attribute (which is /controversial/)
>> - it needs to be specced out a lot more than what I wrote above
>>
>> Maybe Louis could confirm or deny that libc++ has applications for "user-defined trivial" functions, and give some better examples than I did.
>>
>> my $.02,
>> –Arthur
>
>
Received on 2026-01-10 20:16:45
