How can I tell C++ that I want to discard a nodiscard value?

Raymond Chen

C++ lets you add the [[nodiscard]] attribute to a function return value to indicate that the caller must use the result.

Given the declaration

[[nodiscard]] int important();

simply calling the function and allow the value to be discarded produces diagnostics.

void test()
{
    important();
}

clang: ignoring return value of 'int important()', declared with attribute 'nodiscard' [-Wunused-result]

gcc: ignoring return value of 'int important()', declared with attribute 'nodiscard' [-Wunused-result]

msvc: C4834: discarding return value of function with [[nodiscard]] attribute

Explicitly casting to (void) works:

void test()
{
    (void)important();
}

Note that this requires a C-style cast. You cannot static_cast or reinterpret_cast to void.

Another option is to store the result into a variable which is attributed as unused, and then allowing the variable to go out of scope immediately.

void test()
{
    { [[maybe_unused]] auto&& unused = important(); }
}

There is a proposal for C++26 to express the discard with std::ignore:

void test()
{
    std::ignore = important();
}

Although the ability to assign to std::ignore is not formally required, in practice, you have always been able to do it, and the C++ Core Guidelines even recommends it!

The first is tersest, though it suffers from pedagogical issues discussed in the std::ignore proposal. The third is fairly brief and has the benefit of clarity, but suffers from technically not being allowed (though everybody allows it in practice, so much so that even the C++ Core Guidelines were fooled). The second is most verbose, and the only things it has going for it are the pedagogical avoidance of the (void) cast and the language-lawyer avoidance of undocumented use of std::ignore. (In other words, the third option is “technically” the most correct, the best kind of correct.)

There’s an alternate C++26 proposal for expressing the discard with a new [[discard]] attribute.

void test()
{
    [[discard("reason")]] important();
}

Bonus chatter: There’s also a C++26 proposal for anonymous variables. Note that this is not the same as a discarded value, however!

void test()
{
    auto _ = important();
    something_else();
}

This creates an anonymous variable (indicated by _) which is not discarded. The return value of important() is held in the anonymous variable, and that value destructs at the end of the function, after something_else() returns.

10 comments

Discussion is closed. Login to edit/delete existing comments.

  • Stuart Ballard 0

    The idiom of assigning to std::ignore looks weird to me, from a non-C++ programmer perspective. Would it be possible to define a function that could be used as my_ignore(important())? Something like (based on the “possible implementation” of std::ignore here)

    template <typename T>
    constexpr void my_ignore(T&&) const noexcept {}

    Would this be less idiomatic in C++ than assigning to std::ignore?

  • Denis Emelianov 1

    And next we will have a proposal for [[nodiscard_even_with_ignore]] ?

    • Yukkuri Reimu 0

      Yeah exactly my question. How many levels of this before you ask if it was really such a good idea to begin with?

    • W L 1

      This does not create a matryoshka doll.[[nodiscard]] is already an agreement, API designers expressed their wishes, and API users made their own choices knowing this wish.
      Moreover, no matter what you do, c style cast is always an available emergency exit.

  • DB 1

    > Note that this requires a C-style cast. You cannot static_cast or reinterpret_cast to void.

    Can’t we? MSDN:

    > Any expression can be explicitly converted to type void by the static_cast operator.

  • 紅樓鍮 0

    Bonus chatter: In Rust you can just write

    let _ = important();

    (And Rust really will destruct the return value of important() before it goes on to execute the next statement, which is different from the behavior of a regular let variable = ..; which is to keep the value alive until variable goes out of scope.)

    (This is also doable in other languages that have the wildcard pattern _, such as OCaml.)

    Bonus bonus chatter: I thought there’s a C++ proposal about _?

    • W L 0

      Single ‘_’ is already a legal identifier in C++. I think it would be somewhat difficult to add new special semantics to it.

      • DB 1

        Difficult and yet not impossible, since the proposal was accepted into C++26; see P2169R4 here

        • Raymond ChenMicrosoft employee 0

          The catch is that anonymous variables are not discarded!

  • Markus Schaber 0

    As much as I love reading your blogs: The more of your C++ articles I read, the more I’m convinced that C++ should die.
    Most of them are actually “how to fight the language and standard library” aka “how to solve problems which simply don’t exist in sane languages”, instead of “how to solve that tricky, exotic problem” or “how to do this and that on my platform”.
    That’s completely not your fault, as you just write about interesting and tricky problems which come onto your (and your coworkers) desk.

    But having almost 40 years of programming experience, and having worked as a C/C++ dev fulltime for 5 years back then in my dark youth, my impression is that C++ is extraordinarily complicated and complex to grasp (for both the user and those poor guys who have to create a compiler for that beast), instead of being supportive and guiding the dev to the right way.

    And the more brain cells are required to fight the language and its pitfalls, the lower the productivity, and the higher the risk for bugs and security issues.

    My hope is that Languages such as C#, Scala, Rust, Go and Swift will finally replace C/C++. All of them (and some more) are powerful, safe and more understandable replacements for application development, and at least Rust is also a fully capable systems programming language.

Feedback usabilla icon