How can I intentionally discard a [[nodiscard]] return value?

31.3k Views Asked by At

Say I have

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    foo ();
}

then

error: ignoring return value of ‘int foo()’, declared with attribute nodiscard [-Werror=unused-result]

but if

int x = foo ();

then

error: unused variable ‘x’ [-Werror=unused-variable]

Is there a clean way of telling the compiler "I want to discard this [[nodiscard]] value"?

6

There are 6 best solutions below

0
On BEST ANSWER

The WG14 nodiscard proposal discusses the rationale for allowing the diagnostic to be silenced by casting to void. It says casting to void is the encouraged (if non-normative) way to silence it which follows what the existing implementation do with __attribute__((warn_unused_result)):

The [[nodiscard]] attribute has extensive real-world use, being implemented by Clang and GCC as __attribute__((warn_unused_result)) , but was standardized under the name [[nodiscard]] by WG21. This proposal chose the identifier nodiscard because deviation from this name would create a needless incompatibility with C++.

The semantics of this attribute rely heavily on the notion of a use, the definition of which is left to implementation discretion. However, the non-normative guidance specified by WG21 is to encourage implementations to emit a warning diagnostic when a nodiscard function call is used in a potentially-evalulated discarded-value expression unless it is an explicit cast to void. This means that an implementation is not encouraged to perform dataflow analysis (like an initialized-but- unused local variable diagnostic would require). ...

The C++ way would be static_cast<void>.

See the draft C++ standard [[dcl.attr.nodiscard]p2:

[ Note: A nodiscard call is a function call expression that calls a function previously declared nodiscard, or whose return type is a possibly cv-qualified class or enumeration type marked nodiscard. Appearance of a nodiscard call as a potentially-evaluated discarded-value expression is discouraged unless explicitly cast to void. Implementations should issue a warning in such cases. This is typically because discarding the return value of a nodiscard call has surprising consequences. — end note]

This is a note, so non-normative but basically this is what existing implementations do with __attribute__((warn_unused_result)). Also, note a diagnostic for nodiscard is also also non-normative, so a diagnostic for violating nodiscard is not ill-formed but a quality of implementation just like suppressing via a cast to void is.

see the clang document on nodiscard, warn_unused_result:

Clang supports the ability to diagnose when the results of a function call expression are discarded under suspicious circumstances. A diagnostic is generated when a function or its return type is marked with [[nodiscard]] (or __attribute__((warn_unused_result))) and the function call appears as a potentially-evaluated discarded-value expression that is not explicitly cast to void.

0
On

With Boost:

#include <boost/core/ignore_unused.hpp>
int main ()
{
    boost::ignore_unused(foo ());
}

boost::ignore_unused takes its parameter(s) by reference to const, so the argument must be something you can bind to a reference to const. I'm fairly sure that anything that can be a function return type (other than void, of course!) should be OK here.

2
On

Cast it to void:

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    static_cast<void>(foo());
}

This basically tells the compiler "Yes I know I'm discarding this, yes I'm sure of it."

4
On

You can also tag the returned int with another tag:

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    [[maybe_unused]] int i = foo ();
}

Could be useful if you have some debug-only code that requires the value.

3
On

CppCoreGuidelines suggest using std::ignore:

Never cast to (void) to ignore a [[nodiscard]] return value. If you deliberately want to discard such a result, first think hard about whether that is really a good idea (there is usually a good reason the author of the function or of the return type used [[nodiscard]] in the first place). If you still think it's appropriate and your code reviewer agrees, use std::ignore = to turn off the warning which is simple, portable, and easy to grep.

This is almost the same as boost::ignore_unused suggested in another answer, but out of std:: box.

However, there are drawbacks of using std::ignore:

  • Like other such helper function, it would instantiate, spending compile time, and be called (in non-optimized debug), spending run time
  • std::ignore is intended for another purpose
  • std::ignore is not even guaranteed to suppress the warning
2
On

I use a (empty) helper function "discard"

template<typename T>
void discard(const T&) {}

[[nodiscard]] int foo ()
{
    return 0;
}

int main ()
{
    discard(foo());
}

to intentionally discard a [[nodiscard]] value.