55

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"?

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79
spraff
  • 32,570
  • 22
  • 121
  • 229
  • 3
    Sorry if I'm nosy, but *why* do you want to discard the value? If it makes sense to run the code and discard the value, the function shouldn't be `[[nodiscard]]`, right? I *can* think of up two reasons to call a `[[nodiscard]]`function for side effects, unit testing and cache warming. In the UT case, you normally want to compare the return value against some expected result, though. – Arne Vogel Dec 03 '18 at 10:14
  • 1
    In my particular case, a list container erase method returns the iterator to the subsequent list node, but that subsequent node iterator is already known to the calling function. – spraff Dec 03 '18 at 11:34

6 Answers6

51

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."

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • 4
    If only it had been made mandatory in the language _years, even decades_, ago to have to cast-to-void ignored return values! Think how many bugs could have been averted in the edit-compile cycle before even getting to link/run/debug (much less production)!! – davidbak Dec 02 '18 at 16:11
  • 6
    @davidbak Except that there was no `void` in the original C language, and it is common practice to ignore the return value from some library functions (like `printf`). Adding a requirement to explicitly discard the return value from these functions would have broken almost every program in existence at the time. And made for some very inelegant looking code. – 1201ProgramAlarm Dec 03 '18 at 03:45
36

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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
21

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.

Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62
  • I just turn off the unused-variable warning with a pragma, globally. There is no more useless warning from compilers than that one. Oh wait, yes there is: Unused parameter (with the implied suggestion that you remove the parameter's _identifier_ from the signature)! – davidbak Dec 02 '18 at 16:17
  • 6
    Actually, I don't agree. I turned the warning on on purpose on my app, because I don't want to have clutter everywhere, except maybe when I know that with this attribute. Different policies, different approaches ;) – Matthieu Brucher Dec 02 '18 at 16:19
  • 2
    I liked this solution better than casting to void which I find a bit unintuitive. I also don't find the unused variable/parameter to be useless, in fact, it sometimes catches a bug in a large legacy code base where functions are in the thousands of lines (yeah I know x-). Just my 2c. – AndersK Dec 02 '18 at 16:23
  • The reason for my downvote is, that you actually bind the return value of `foo` and not discard it. The difference is in the drop order or that variable. If you bind it, the value will be dropped after the variable goes out of scope, if you discard it, the value will be dropped immediately. – hellow May 04 '23 at 06:36
21

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
Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79
  • "`std::ignore` is not even guaranteed to suppress the warning" no, but insofar as it uses the result, it *must* suppress th warning that the result is unused... I get that this is not the original intended purpose of `std::ignore`, but it seems clearly required to work for it. – underscore_d Jun 30 '23 at 09:11
  • 1
    @underscore_d, yes, it works for usual compiler warnings which formally understands that the result is used by `std::ignore`, but it might be an issue for a static analysis tool which tries to track the control flow to some extent – Alex Guteniev Jun 30 '23 at 09:50
12

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.

wolfseifert
  • 121
  • 1
  • 2
3

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366