C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Dummy names for dummy objects

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Thu, 29 Jun 2023 11:02:29 -0400
On Thu, Jun 29, 2023 at 10:20 AM Harald Achitz via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> my concern is that auto _ = f();
>
> * will be abused by lazy developers to not care about nodiscard return
> values, instead of a clear syntax that makes it obvious that for any reason
> a nodiscard return value is ignored
>
> Today, there is a more verbose syntax required, and that makes it more
> readable to see what is going on in code reviews and when reading the code.
>
> * makes it impossible to catch unhandled return values if for any reason
> nodiscard gets added to a function and _ is already in use
>
I think you're misunderstanding the merged proposal and possibly also the
status quo.
(I don't particularly care for the merged proposal either, but only because
I don't like making `_` into a magic identifier; it's one more thing to
teach and I don't think its benefit outweighs that cost. Nonetheless...)
Suppose in C++26 someone writes:
    auto _ = f();
This unambiguously means they want to save `f()`'s value until the end of
scope, but that they won't use it for anything otherwise.
"But what if `f` was [[nodiscard]]?"
That's fine. The programmer *is* using `f`'s value; they're saving it until
the end of scope.
"But shouldn't this still be diagnosed? They didn't say [[maybe_unused]]!"
No, that's fine. `_` is unused by definition. If the programmer wanted to
get a -Wunused warning, they wouldn't have called the variable `_`.
    auto x = f(); // OK, now we get a -Wunused warning if `x` is never used
"So people will use `auto _ = f()` to conveniently suppress nodiscardness?"
No, people already use
    (void)f();
if all they want to do is suppress nodiscardness. They'll keep doing that.
The reason to use
    auto _ = f();
is that you want to save `f()`'s value until the end of scope (i.e. *not*
discard and destroy it immediately). If you just want to discard f()'s
value, you wouldn't do that.

In fact, personally I might hope for a *compiler warning* whenever someone
writes
    int _ = f();
(or any trivially destructible type like that), because in that case
there's no point to capturing `_` at all. That line of code probably
indicates that the programmer doesn't understand the point of capturing
`_`, which is to prolong the lifetime of a value whose destruction *will
have side effects*.

> My suggestion would simply be, if a function is nodiscard, the _ to ignore
> it's returnvalue should not compile (keep the warning).
>
That is the exact opposite of the point.
    auto lk = my::lock_guard(m); // my::lock_guard is [[nodiscard]]
compiles today without warning, and has the right behavior, but requires
choosing a unique name `lk`.
    auto _ = my::lock_guard(m); // my::lock_guard is [[nodiscard]]
compiles in C++26 and does *not* require choosing any unique name, so, it's
better.
If the compiler gave a diagnostic on the latter line, then we couldn't
write it, and so the entire `_` feature would have no point at all.

Remember:
> Use a cast to (void) to suppress nodiscardness.
> Use `_` to prolong a lifetime to the end of scope without choosing a
unique name.

HTH,
Arthur

Received on 2023-06-29 15:02:43