C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [PXXXXR0] Add a New Keyword `undecl`

From: David Brown <david.brown_at_[hidden]>
Date: Thu, 11 Dec 2025 09:48:05 +0100
On 10/12/2025 21:13, Simon Schröder via Std-Proposals wrote:
>
>
>> On Dec 10, 2025, at 9:46 AM, David Brown via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> 1. Copy-and-paste code (redeclaration without undecl) :
> This variant I don’t really like. It would be too easy to accidentally shadow a variable. If I have working code and I accidentally redeclare a variable (possibly with the same type) right between the declaration of the old variable and the previously last use of the variable, I will break that code without a compiler error. In my opinion it is a good thing to have compiler errors when you try to do something stupid. (Maybe we could allow shadowing without undecl and then unshadow using undecl. Anyway, it would be much better if you’d had to be explicit about shadowing, e.g. with an attribute [[shadow]]. Nothing accidental about that.) BTW, if you want a variable you can shadow, use _.

Those are all good arguments. Features that can make it easier to write
some kinds of correct code often make it easier to write some kinds of
incorrect code.

The name-independent identifier (if that is the correct term?) _ won't
work here for two reasons :

     auto _ = get_next();
     if (_) do_something1(*_);

     auto _ = get_next();
     if (_) do_something2(*_);

The first reason is technical - while it's fine to re-declare "auto _ ="
as often as you want, trying to use _ in the second "if (_) ..." fails
as "_" has been defined multiple times and is ambiguous. The second
reason is that the line using "_" looks terrible and is hard to read
unless you are a Perl refugee!

But I'd be very much in favour of a solution that involved an extra
indicator that we really want to shadow previous declarations.
Unfortunately it cannot be an attribute - currently (AFAIK - there may
be proposals dealing with this) attributes in C++ can be ignored, so
they can't be used for such cases. My suggestion then would be to use
"override". It is not currently a keyword, but it's been an "identifier
with special meaning" for so long that it's highly unlikely to be used
as a user identifier in existing code.

     override auto value = get_next();
     if (value) do_something1(*value);

     override auto value = get_next();
     if (value) do_something2(*value);

(It might be good to have a constraint that the type of overridden
variables has a trivial destructor - then there is no worry about when
the lifetime ends and the destructor is triggered.)


>>
>> 2. Explicitly ending the lifetime of a variable and making it unusable:
>>
> Other languages (e.g. Rust) use the ‘keyword’ ‘drop’ for this. I’m not sure if ‘drop’ will collide with common variable names (or more likely with function names). ‘undecl’ certainly looks like it is not yet used in any C++ code. As has been replied already: std::optional already allows to end the lifetime of a variable early (but without the added “benefit” of being able to reuse its name (if it’s not the same type)).
>>

Certainly std::optional can be used for the first half of this use-case,
but it does nothing for the second half - making later use of the
variable an error. My suggestion was :

     auto x = get_x();
     do_something(x);
     undecl x; // x is destructed here
     do_something_else(x); // Compile-time error

With std::optional, you have :

     auto x = std::optional(get_x());
     do_something(*x);
     x.reset(); // x is destructed here
     do_something_else(*x); // Compile-time ok, run-time UB

So while std::optional<> does let you explicitly choose the end-point
for the object's lifetime, the code is less clear (you don't actually
want an optional type, so why is "optional" there? And you need an
extra indirection everywhere) and more importantly, the code is much
less safe.

(Being able to re-declare the name after "undecl x" is, as you say, not
without its disadvantages in code clarity. That could be considered as
an orthogonal question.)

>> 3. Const-locking identifiers (redeclaration without undecl) :
>>
> This is a nice idea I could get behind.
>>
>> auto x = get_x();
>> change_x(&x);
>> const auto x = x;
>> change_x(&x); // Compile-time error
>>
> Currently syntax like the line
> const auto x = x;
> would first declare a variable ‘x’ and then assign it to itself. We should have some other syntax for this feature. It is currently already possible to write this kind of buggy code if ‘x’ comes from an outer scope. There is no reason for this to behave differently if we shadow an ‘x’ from the same scope. And certainly someone is “relying” on this bug and we’d break someone’s code if we fix this.

Self-initialisation is an idiom that is sometimes used as a way to
disable compiler warnings about using uninitialised variables. (Don't
blame me - /I/ don't use that idiom!) So it is certainly something that
is used in real code, albeit rarely and questionably.

Borrowing from point 1, I'd suggest :

 override const auto x = x;

You could even handle point 2 with :

 override void x;

to "redeclare" x as being of type "void", thus invoking the destructor
of the old x and giving you a new x with type "void" - trying to do
anything with it (except perhaps "x;") would result in an error, as desired.

Received on 2025-12-11 08:48:10