Date: Thu, 13 Aug 2026 14:51:57 +0200
Are you aware of the existing rules for _ in C++26? Here is the original
proposal:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2169r4.pdf
If you want to change/extend the behavior, it has to be compatible with the
current standard (backwards compatibility).
On Thu, Aug 13, 2026 at 2:32 PM Connor Song via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi all,
>
> I would like to propose a different way of thinking about _.
>
> I don't think _ should mean merely:
>
> this variable is unused.
>
> A more general interpretation might be:
>
> this entity does not have an identity that the program is expected to
> remember.
>
> Or, less formally:
>
> I don't need to remember this entity, so I don't need to give it a
> persistent identifier.
>
> I think this interpretation naturally covers two different cases which are
> currently treated as somewhat separate concepts.
>
> The first case is the familiar one: the entity is not important and can
> simply be ignored.
>
> For example:
>
> auto [result, _] = resolve();
>
> Here _ means that the second result exists, but its identity is
> irrelevant to the rest of the program.
>
> But there is another case:
>
> The entity is important briefly, but its identity should not remain
> available indefinitely.
>
> For example:
>
> Socket _ = connect();
>
> TlsConnection secure(_);
>
> Widget _ = make_widget();
>
> secure.send(data);
>
> Suppose TlsConnection borrows the Socket.
>
> The socket therefore cannot simply be destroyed after constructing secure;
> it must remain alive according to the normal C++ lifetime rules.
>
> However, after constructing secure, there may be no reason for later code
> to retain a stable name for the underlying socket.
>
> Writing:
>
> Socket raw = connect();
> TlsConnection secure(raw);
>
> gives the underlying socket a persistent identity, and later code can
> accidentally use it:
>
> raw.send(data); // accidentally bypasses TLS
> secure.send(data);
>
> Using _ expresses something different:
>
> Socket _ = connect();
> TlsConnection secure(_);
>
> The object is needed, and the binding is temporarily needed, but the
> identity is not something later code is expected to remember.
>
> This suggests extending the existing name-independent _ model.
>
> Conceptually, I have the following rule in mind:
>
> Multiple name-independent declarations named _ may inhabit the same
> scope. An id-expression _ denotes the most recently preceding such
> declaration.
>
> The previous objects would not be destroyed when a new _ is declared.
> Their lifetimes would remain completely unchanged.
>
> Only the binding denoted by _ changes.
>
> For example:
>
> Socket _ = connect(); // object A
> TlsConnection secure(_); // refers to A
>
> Widget _ = make_widget(); // object B, `_` now denotes B
>
> Object A remains alive as usual. It merely no longer has the current _
> binding.
>
> There is one additional rule I think is necessary:
>
> A new _ binding becomes the current _ only after its initializer has been
> evaluated.
>
> This permits the previous ephemeral binding to be used to initialize the
> next one:
>
> auto _ = parse(input); // Parsed
> auto _ = validate(_); // Validated
> auto _ = normalize(_); // Normalized
> consume(_);
>
> In:
>
> auto _ = validate(_);
>
> the _ inside the initializer denotes the previous binding.
>
> Only after initialization succeeds does the newly declared object become
> the current _.
>
> I think this gives ordinary function calls a useful pipeline-like form
> without introducing pipeline-specific syntax:
>
> auto _ = receive();
> auto _ = decrypt(_);
> auto _ = authenticate(_);
> process(_);
>
> Conceptually, this means:
>
> receive
> |
> v
> _
> |
> decrypt
> |
> v
> _
> |
> authenticate
> |
> v
> _
>
> Each intermediate object may have a completely different type.
>
> The important part is not that these objects are "unused". They clearly
> are used.
>
> Rather, none of their individual identities are important enough to be
> named and remembered.
>
> That leads to what I think is the central distinction:
>
> auto parsed = parse(input);
>
> says:
>
> this result has an identity worth naming and remembering.
>
> Whereas:
>
> auto _ = parse(input);
>
> would say:
>
> this result may be needed temporarily, but its identity is intentionally
> non-persistent.
>
> A subsequent _ declaration therefore does not unexpectedly "shadow" a
> meaningful identifier. Rebinding is part of the contract of _ itself.
>
> This is also why I think the normal objection to repeated declarations
> does not apply in quite the same way.
>
> Silently changing what an ordinary name denotes would be dangerous:
>
> double x = compute();
>
> // ...
>
> int x = 42;
>
> // ...
>
> return x;
>
> The name x implies stable identity.
>
> But _ would imply precisely the opposite: code should not rely on that
> binding having persistent identity.
>
> So I would summarize the idea as:
>
> _ should not mean "this variable is unused".
>
> _ should mean "this entity does not have an identity that the program is
> expected to remember."
>
> "I don't care about this entity" is then one special case:
>
> auto [value, _] = f();
>
> "I need this entity briefly, but don't need to remember it afterwards" is
> another:
>
> Socket _ = connect();
> TlsConnection secure(_);
>
> And a sequence of ephemeral intermediate values naturally gives us:
>
> auto _ = parse(input);
> auto _ = validate(_);
> auto _ = normalize(_);
> consume(_);
>
> I would initially keep this proposal strictly about name binding.
>
> In particular, I am *not* suggesting that using _ implicitly moves from
> the previous object, changes its value category, shortens its lifetime, or
> causes early destruction.
>
> For:
>
> auto _ = validate(_);
>
> the _ in the initializer would have the same value category it has under
> ordinary C++ expression rules.
>
> Any storage reuse, copy elision, dead-store elimination, or similar
> optimization should remain an implementation matter under the usual rules.
>
> The proposal would therefore be relatively narrow:
>
> 1.
>
> _ represents a non-persistent binding rather than merely an unused
> variable.
> 2.
>
> Multiple _ declarations may coexist in one scope.
> 3.
>
> _ refers to the most recently established binding.
> 4.
>
> A new binding becomes current only after its initializer has completed.
> 5.
>
> Rebinding _ has no effect on the lifetime of previously bound objects.
>
> I am interested in whether this is a useful generalization of the existing
> name-independent declaration model, and in particular whether the current
> ambiguity after multiple _ declarations is considered an important safety
> property that should be preserved.
>
> Best,
> Connor
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
proposal:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2169r4.pdf
If you want to change/extend the behavior, it has to be compatible with the
current standard (backwards compatibility).
On Thu, Aug 13, 2026 at 2:32 PM Connor Song via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi all,
>
> I would like to propose a different way of thinking about _.
>
> I don't think _ should mean merely:
>
> this variable is unused.
>
> A more general interpretation might be:
>
> this entity does not have an identity that the program is expected to
> remember.
>
> Or, less formally:
>
> I don't need to remember this entity, so I don't need to give it a
> persistent identifier.
>
> I think this interpretation naturally covers two different cases which are
> currently treated as somewhat separate concepts.
>
> The first case is the familiar one: the entity is not important and can
> simply be ignored.
>
> For example:
>
> auto [result, _] = resolve();
>
> Here _ means that the second result exists, but its identity is
> irrelevant to the rest of the program.
>
> But there is another case:
>
> The entity is important briefly, but its identity should not remain
> available indefinitely.
>
> For example:
>
> Socket _ = connect();
>
> TlsConnection secure(_);
>
> Widget _ = make_widget();
>
> secure.send(data);
>
> Suppose TlsConnection borrows the Socket.
>
> The socket therefore cannot simply be destroyed after constructing secure;
> it must remain alive according to the normal C++ lifetime rules.
>
> However, after constructing secure, there may be no reason for later code
> to retain a stable name for the underlying socket.
>
> Writing:
>
> Socket raw = connect();
> TlsConnection secure(raw);
>
> gives the underlying socket a persistent identity, and later code can
> accidentally use it:
>
> raw.send(data); // accidentally bypasses TLS
> secure.send(data);
>
> Using _ expresses something different:
>
> Socket _ = connect();
> TlsConnection secure(_);
>
> The object is needed, and the binding is temporarily needed, but the
> identity is not something later code is expected to remember.
>
> This suggests extending the existing name-independent _ model.
>
> Conceptually, I have the following rule in mind:
>
> Multiple name-independent declarations named _ may inhabit the same
> scope. An id-expression _ denotes the most recently preceding such
> declaration.
>
> The previous objects would not be destroyed when a new _ is declared.
> Their lifetimes would remain completely unchanged.
>
> Only the binding denoted by _ changes.
>
> For example:
>
> Socket _ = connect(); // object A
> TlsConnection secure(_); // refers to A
>
> Widget _ = make_widget(); // object B, `_` now denotes B
>
> Object A remains alive as usual. It merely no longer has the current _
> binding.
>
> There is one additional rule I think is necessary:
>
> A new _ binding becomes the current _ only after its initializer has been
> evaluated.
>
> This permits the previous ephemeral binding to be used to initialize the
> next one:
>
> auto _ = parse(input); // Parsed
> auto _ = validate(_); // Validated
> auto _ = normalize(_); // Normalized
> consume(_);
>
> In:
>
> auto _ = validate(_);
>
> the _ inside the initializer denotes the previous binding.
>
> Only after initialization succeeds does the newly declared object become
> the current _.
>
> I think this gives ordinary function calls a useful pipeline-like form
> without introducing pipeline-specific syntax:
>
> auto _ = receive();
> auto _ = decrypt(_);
> auto _ = authenticate(_);
> process(_);
>
> Conceptually, this means:
>
> receive
> |
> v
> _
> |
> decrypt
> |
> v
> _
> |
> authenticate
> |
> v
> _
>
> Each intermediate object may have a completely different type.
>
> The important part is not that these objects are "unused". They clearly
> are used.
>
> Rather, none of their individual identities are important enough to be
> named and remembered.
>
> That leads to what I think is the central distinction:
>
> auto parsed = parse(input);
>
> says:
>
> this result has an identity worth naming and remembering.
>
> Whereas:
>
> auto _ = parse(input);
>
> would say:
>
> this result may be needed temporarily, but its identity is intentionally
> non-persistent.
>
> A subsequent _ declaration therefore does not unexpectedly "shadow" a
> meaningful identifier. Rebinding is part of the contract of _ itself.
>
> This is also why I think the normal objection to repeated declarations
> does not apply in quite the same way.
>
> Silently changing what an ordinary name denotes would be dangerous:
>
> double x = compute();
>
> // ...
>
> int x = 42;
>
> // ...
>
> return x;
>
> The name x implies stable identity.
>
> But _ would imply precisely the opposite: code should not rely on that
> binding having persistent identity.
>
> So I would summarize the idea as:
>
> _ should not mean "this variable is unused".
>
> _ should mean "this entity does not have an identity that the program is
> expected to remember."
>
> "I don't care about this entity" is then one special case:
>
> auto [value, _] = f();
>
> "I need this entity briefly, but don't need to remember it afterwards" is
> another:
>
> Socket _ = connect();
> TlsConnection secure(_);
>
> And a sequence of ephemeral intermediate values naturally gives us:
>
> auto _ = parse(input);
> auto _ = validate(_);
> auto _ = normalize(_);
> consume(_);
>
> I would initially keep this proposal strictly about name binding.
>
> In particular, I am *not* suggesting that using _ implicitly moves from
> the previous object, changes its value category, shortens its lifetime, or
> causes early destruction.
>
> For:
>
> auto _ = validate(_);
>
> the _ in the initializer would have the same value category it has under
> ordinary C++ expression rules.
>
> Any storage reuse, copy elision, dead-store elimination, or similar
> optimization should remain an implementation matter under the usual rules.
>
> The proposal would therefore be relatively narrow:
>
> 1.
>
> _ represents a non-persistent binding rather than merely an unused
> variable.
> 2.
>
> Multiple _ declarations may coexist in one scope.
> 3.
>
> _ refers to the most recently established binding.
> 4.
>
> A new binding becomes current only after its initializer has completed.
> 5.
>
> Rebinding _ has no effect on the lifetime of previously bound objects.
>
> I am interested in whether this is a useful generalization of the existing
> name-independent declaration model, and in particular whether the current
> ambiguity after multiple _ declarations is considered an important safety
> property that should be preserved.
>
> Best,
> Connor
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2026-08-13 12:52:41
