Why cast unused return values to void?

int fn();


void whatever()
{
(void) fn();
}

Is there any reason for casting an unused return value to void, or am I right in thinking it's a complete waste of time?

53586 次浏览

在工作中,我们用它来确认函数有一个返回值,但是开发人员断言忽略它是安全的。既然你把这个问题标记为 C + + ,那么你应该使用 静态播放:

static_cast<void>(fn());

就编译器而言,将返回值强制转换为 void 没有什么意义。

转换为 void 是无成本的,它只是编译器如何处理它的信息。

David 的 回答几乎涵盖了这样做的动机,明确地向其他“开发人员”表明,你知道这个函数返回,但是你明确地忽略了它。

这是一种确保在必要的错误代码总是得到处理的方法。

我认为对于 C + + 来说,这可能是唯一一个我更喜欢使用 C 风格强制转换的地方,因为在这里使用完整的静态强制转换符号感觉有点过头了。最后,如果你正在审查一个编码标准或者正在编写一个,那么明确地声明对重载操作符的调用(不使用函数调用表示法)也应该免除这一点也是一个好主意:

class A {};
A operator+(A const &, A const &);


int main () {
A a;
a + a;                 // Not a problem
(void)operator+(a,a);  // Using function call notation - so add the cast.

这样做的真正原因可以追溯到 C 代码中使用的工具 棉绒

它分析代码,寻找可能的问题并发出警告和建议。如果一个函数返回一个未被检查的值,lint会发出警告,以防这是偶然的。若要在此警告上禁止 lint,请将调用强制转换为 (void)

因为程序强制转换的功能无效是毫无意义的。我还要说明的是,你不应该像 David 在答案中建议的那样,用它来向正在阅读代码的人发出某种信号。如果你想表达你的意图,最好使用评论。添加这样的阵容只会看起来很奇怪,并且会引起对可能原因的疑问。只是我个人的看法。

转换为 void用于抑制未使用的变量和未保存的返回值或表达式的编译器警告。

《标准》(2003)在5.2.9/4中说,

任何表达式都可以显式地转换为“ cv void”类型。

所以你可以写:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);


//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());


//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

所有表格都是有效的,我通常把它缩短如下:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

也没关系。

此外,在验证代码是否符合 MISRA (或其他)标准时,LDRA 等静态分析工具不允许您调用具有返回类型的函数,除非您显式地将返回值强制转换为(void)

由于 c + + 17,我们有了 [[maybe_unused]]属性,可以用它来代替 void强制转换。

C + + 17 [[nodiscard]]

C + + 17使用一个属性标准化了“忽略返回值的业务”。

因此,我希望符合规范的实现只在给定 nodiscard时发出警告,而不发出其他警告。

例如:

Main.cpp

[[nodiscard]] int f() {
return 1;
}


int main() {
f();
}

编译:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

结果:

main.cpp: In function ‘int main()’:
main.cpp:6:6: warning: ignoring return value of ‘int f()’, declared with attribute nodiscard [-Wunused-result]
6 |     f();
|     ~^~
main.cpp:1:19: note: declared here
1 | [[nodiscard]] int f() {
|

以下内容均可避免警告:

(void)f();
[[maybe_unused]] int i = f();

我无法在 f()呼叫中直接使用 maybe_unused:

[[maybe_unused]] f();

提供:

main.cpp: In function ‘int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
6 |     [[maybe_unused]] f();
|     ^~~~~~~~~~~~~~~~

(void)的铸造工作似乎不是强制性的,但在标准中是“鼓励”的: 如何故意丢弃[[ nodiscard ]]返回值?

从警告消息中还可以看到,一个解决警告的“方案”是添加 -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

尽管我当然不会建议像这样忽视全球性的警告。

C + + 20还允许您向 nodiscard添加一个原因,就像在 [[nodiscard("reason")]]中提到的那样: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

GCC warn_unused_result属性

[[nodiscard]]标准化之前,在 C 最终决定标准化属性之前,GCC 用 warn_unused_result实现了完全相同的功能:

int f() __attribute__ ((warn_unused_result));


int f() {
return 1;
}


int main() {
f();
}

它给出了:

main.cpp: In function ‘int main()’:
main.cpp:8:6: warning: ignoring return value of ‘int f()’, declared with attribute warn_unused_result [-Wunused-result]
8 |     f();
|     ~^~

需要注意的是,由于 ANSI C 没有这方面的标准,ANSI C 没有指定哪些 C 标准库函数具有这个属性,因此实现自己决定哪些应该标记为 warn_unuesd_result,这就是为什么一般情况下你必须使用 (void)强制转换来忽略对标准库函数的任何调用的返回,以完全避免在任何实现中出现警告。

在 GCC 9.2.1,Ubuntu 19.10中测试。

在 C + + 11中,你还可以:

std::ignore = fn();

对于标记为 [[nodiscard]]的函数,这应该会得到相同的结果