Explanation of [[nodiscard]] in C++17

11.6k views Asked by At

In a project using C++20, CLion suggested me to add [[nodiscard]] to my const class method definitions, e.g.,

class Test {
public:
    [[nodiscard]] int f(int a, int b) const {
        return a + b;
    }
}

The explanation is

Adds [[nodiscard]] attributes (introduced in C++17) to member functions in order to highlight at compile time which return values should not be ignored.

and I also checked cppreference.com:

If a function declared nodiscard or a function returning an enumeration or class declared nodiscard by value is called from a discarded-value expression other than a cast to void, the compiler is encouraged to issue a warning. ... Appears in a function declaration, enumeration declaration, or class declaration.

If, from a discarded-value expression other than a cast to void,

  • a function declared nodiscard is called, or
  • a function returning an enumeration or class declared nodiscard by value is called, or
  • a constructor declared nodiscard is called by explicit type conversion or static_cast, or
  • an object of an enumeration or class type declared nodiscard is initialized by explicit type conversion or static_cast,

the compiler is encouraged to issue a warning.

I honestly don't really understand why this annotation is needed in this location. Why would the compiler ignore my return values if the caller processes them further? Is there an intuitive explanation of what it tells the compiler exactly and why it's needed?

3

There are 3 answers

3
ypnos On BEST ANSWER

The idea is that if it only makes sense to call your function when you also take its return value, calling it without taking the return value is a programming error. The annotation [[nodiscard]] helps programmers interfacing with your code avoid this error.

In your specific example, your function computes a result without side effects, so the static code analyzer realizes that it would be a good fit for [[nodiscard]]. For example:

Test a;
auto x = a.f(1, 2); // ok
std::cout << "For 1, 2 we get " << a.f(1,2) << std::endl; // also ok
a.f(1, 2); // warning here

Here the compiler is encouraged to warn in the last line about the function call without further processing of the result.

Discussion

An example of good use for [[discard]] can be object methods that manipulate an object, but provide the result in a copy. Example:

DateTime yesterday = DateTime.now().substractOneDay();
std::cout << "Yesterday was " << yesterday.nameOfDay() << std::endl;

vs:

DateTime yesterday = DateTime.now();
yesterday.substractOneDay();
std::cout << "Yesterday was " << yesterday.nameOfDay() << std::endl;

A [[nodiscard]] would tell the programmer of the second example, that they are using it wrong.

2
Kevin Spaghetti On

[[nodiscard]] is useful when you have class methods that return something and you want to highlight that the method does not work in place but instead returns something.
For example if you had a linked list a possible reverse method could be marked [[nodiscard]] to signal that the method returns a new list.

It helps avoid code like this:

List l{1, 2, 3, 4};
l.reverse();
std::cout << l;

>> 1, 2, 3, 4 //Why is this not working?

To me it seems that the most useful place to employ [[nodiscard]] is when you have a method/function that modifies the object/parameters but also returns something (so the method/function cannot be const).

List l{1, 2, 3, 4};
l.doubleEveryElementAndSum(); //the sum returned is silently ignored
std::cout << l;

>> 2, 4, 6, 8

The fact that CLion highlights the method and tells you that the method could be marked [[nodiscard] has nothing to do with CLion or the compiler but with the tool clang-tidy. This tool scans your code and checks for things that you can improve, CLion then visualizes the results of this tool.
When you compile your code the compiler will read the annotation and issue a warning whenever it finds code that ignores the return value of the function marked [[nodiscard]].

My suggestion is to not pollute your code by putting [[nodiscard]] everywhere as CLion (clang-tidy) suggests. If you find the warnings annoying you can disable them in the clang-tidy settings.

2
Sam Varshavchik On

No, this annotation is not needed in this case.

The suggestion to add it came from your text editor. Your text editor is not a C++ compiler. Only a C++ compiler fully understands C++ source code. Your C++ text editor merely has a few, simple, rules for adding suggestions that it thinks are helpful in improving code. The specific reason why it might've shown this suggestion are mere speculation, without knowing exactly what your text editor's logic does.

which return values should not be ignored.

The key word there is "should". It is not an error to ignore values returned from functions or class methods. That, by itself, does not make code ill-formed. Now, it's true that in some instances ignoring return values can lead to issues. But that must require other factors to come into play. For example, ignoring the return value from read() and blissfully assuming that anything was actually read from a file or a socket, is nearly always a bug.