c++c++17compiler-warningsnodiscardc++-attributes

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


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?


Solution

  • 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 [[nodiscard]] 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.