C++17 中 [[nodiscard]] 的解释

问题描述 投票:0回答:3

在使用 C++20 的项目中,CLion 建议我将

[[nodiscard]]
添加到我的 const 类方法定义中,例如,

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

解释是

向成员函数添加 [[nodiscard]] 属性(在 C++17 中引入),以便在编译时突出显示哪些返回值不应被忽略。

我还检查了cppreference.com

如果从废弃值表达式(而不是强制转换为 void)调用声明为 nodiscard 的函数或按值返回声明为 nodiscard 的枚举或类的函数,则鼓励编译器发出警告。 ... 出现在函数声明、枚举声明或类声明中。

如果,从丢弃值表达式而不是强制转换为 void,

  • 调用声明为 nodiscard 的函数,或者
  • 调用返回按值声明为 nodiscard 的枚举或类的函数,或者
  • 通过显式类型转换或 static_cast 调用声明为 nodiscard 的构造函数,或者
  • 声明为 nodiscard 的枚举或类类型的对象通过显式类型转换或 static_cast 进行初始化,

鼓励编译器发出警告。

老实说,我不太明白为什么这个位置需要这个注释。如果调用者进一步处理它们,为什么编译器会忽略我的返回值?是否有一个直观的解释来解释它到底告诉编译器什么以及为什么需要它?

c++ c++17 compiler-warnings nodiscard c++-attributes
3个回答
50
投票

这个想法是,如果只有当您还获取函数的返回值时调用函数才有意义,那么在不获取返回值的情况下调用它就是一个编程错误。注释

[[nodiscard]]
可以帮助与您的代码交互的程序员避免此错误。

在您的具体示例中,您的函数计算结果没有副作用,因此静态代码分析器意识到它非常适合

[[nodiscard]]
。例如:

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

这里鼓励编译器在最后一行警告函数调用,而不进一步处理结果。

讨论

[[nodiscard]]
的一个很好用的例子是操作对象的对象方法,但在副本中提供结果。示例:

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

与:

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

A

[[nodiscard]]
会告诉第二个示例的程序员,他们使用的是错误的。


7
投票
当您有返回某些内容的类方法并且您想强调该方法不能就地工作而是返回某些内容时,

[[nodiscard]]
非常有用。
例如,如果您有一个链接列表,则可以将可能的
reverse
方法标记为
[[nodiscard]]
以表示该方法返回一个新列表。

它有助于避免这样的代码:

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

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

对我来说,使用

[[nodiscard]]
最有用的地方似乎是当你有一个方法/函数可以修改对象/参数但也返回一些东西时(所以方法/函数不能是const)。

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

>> 2, 4, 6, 8

CLion 突出显示该方法并告诉您该方法可以标记为

[[nodiscard]
,这一事实与 CLion 或编译器无关,而是与 clang-tidy 工具有关。该工具会扫描您的代码并检查您可以改进的内容,然后 CLion 可视化该工具的结果。
当您编译代码时,编译器将读取注释,并在发现忽略标记为
[[nodiscard]]
的函数的返回值的代码时发出警告。

我的建议是不要像 CLion (clang-tidy) 建议的那样将

[[nodiscard]]
放在任何地方,从而污染你的代码。如果您觉得这些警告很烦人,您可以在 clang-tidy 设置中禁用它们。


1
投票

不,在这种情况下不需要此注释。

添加它的建议来自您的文本编辑器。您的文本编辑器不是 C++ 编译器。只有 C++ 编译器才能完全理解 C++ 源代码。您的 C++ 文本编辑器只有一些简单的规则,用于添加它认为有助于改进代码的建议。它可能显示此建议的具体原因仅仅是猜测,并不确切知道您的文本编辑器的逻辑是做什么的。

哪些返回值不应被忽略。

这里的关键词是“应该”。忽略从函数或类方法返回的值并不是错误。这本身并不会使代码格式错误。现在,确实在某些情况下忽略返回值可能会导致问题。但这必须需要其他因素发挥作用。例如,忽略

read()
的返回值并幸福地假设实际上从文件或套接字读取任何内容,几乎总是一个错误。

© www.soinside.com 2019 - 2024. All rights reserved.