C++ Logo

std-proposals

Advanced search

[std-proposals] Generalizing `_` as a non-persistent binding

From: Connor Song <perdixky_at_[hidden]>
Date: Thu, 13 Aug 2026 20:31:52 +0800
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

Received on 2026-08-13 12:32:13