将变量转换为`void`类型到底是什么意思?

75

常用的语句,例如(void)x;可以用来抑制未使用变量x的警告。但是,如果我尝试编译以下代码,我会得到一些我不太理解的结果:

int main()
{
    int x;
    (short)x;
    (void)x;
    (int)x;
}

使用g++编译此内容,我得到以下警告:

$ g++ test.cpp -Wall -Wextra -o test
test.cpp: In function ‘int main()’:
test.cpp:4:13: warning: statement has no effect [-Wunused-value]
     (short)x;
             ^
test.cpp:6:11: warning: statement has no effect [-Wunused-value]
     (int)x;
           ^

所以我得出结论,将类型转换为void与转换为任何其他类型非常不同,无论目标类型是decltype(x)还是其他不同的类型。我对可能的解释的猜测是:

  • 这只是一个惯例,(void)x;而不是其他类型的转换将抑制警告。所有语句都没有任何效果。
  • 这种差异与void x;不是有效语句而short x;是有关的。

如果有的话,哪个更正确?如果没有,那么编译器警告的差异如何解释?


8
这只是编译器的一种惯例,这种强制转换可以抑制警告。C++标准根本没有提到“语句无效”的警告。 - M.M
@cigien 尽管这两个问题表面上看起来相似,但这个问题询问的是将类型转换为 void 和将类型转换为其他类型之间差异的机制,而另一个问题则询问为什么要进行 void 类型转换。然而,由于这个问题已经有了令人满意的答案,我不会修改它并投票重新开放。这条评论只是为了回答自动生成的“这个回答解决了你的问题吗?” TLDR:没有。 - Ruslan
我明白你的意思,通常来说,“什么”和“为什么”问题应该分开。但在这种情况下,“它是做什么的”与“为什么要这样做”紧密相关,所以我认为将它们联系起来是值得的。除了抑制警告之外,没有任何理由这样做(即强制转换为空),两个帖子上的所有答案基本上都是相同的。 - cigien
4个回答

76
将表达式转换为void类型是用来抑制编译器警告的。《标准》在§5.2.9/4中说道,
任何表达式都可以显式转换为“cv void”类型。表达式的值将被丢弃。

请问你能更新一下链接吗?它已经不再指向标准页面了,谢谢。 - JamesT

33

这个陈述:

(void)x;

说“忽略x的值”。不存在名为void的类型-它是一种类型的缺失。所以它与这个有很大的不同:

(int)x;

这句话的意思是:“将x看作整数处理。”当忽略所得到的整数时,如果启用了警告,则会收到警告。

如果你忽略了一些什么东西,而那个东西什么都不是,在GCC中就不会被视为问题--这是有充分理由的,因为在C和C ++中,强制转换为void是一个明确忽略变量的习惯用法。


8
我很确定存在“void”这种类型。请见[basic.types]。 - Kerrek SB
1
@Sacert: (void)x 是将一个变量转换为 void。你自己说了。但是一旦你这样做了,你就不能再对它进行任何操作,因为 void 表达式没有值。 - John Zwinck
4
将返回结果强制转换为void ((void)foo()) 更加有用,这表示你有意忽略返回值。我只能想象在某些 #ifdef 代码块内部使用变量且希望在代码未被包含时抑制警告时,才需要将变量转换为void。 - Martin Bonner supports Monica
1
@MartinBonner:是的,将变量转换为void通常是在变量仅在assert()中使用时完成的,因此在发布版本中会产生警告。 - John Zwinck
1
“没有void这种类型”是错误的。void是一种无法完成的不完整类型。它一种类型。 - Ayxan Haqverdili
显示剩余6条评论

12

标准不要求为未使用的局部变量或函数参数生成警告(标准术语中的“诊断”)。同样,它也没有规定如何抑制此类警告。在C和后来的C++社区中,将变量表达式转换为void以抑制此警告已成为一种习惯用法,因为结果不能以任何方式使用(除了例如(int)x),因此相应的代码可能并不缺失。例如:

(int)x;  // maybe you meant f((int)x);
(void)x; // cannot have intended f((void)x);
(void)x; // but remote possibility: f((void*)x);

就我个人而言,我仍然觉得这种惯例太过晦涩难懂,因此我更喜欢使用函数模板:

template<typename T>
inline void ignore(const T&) {} // e.g. ignore(x);

忽略函数参数的惯用方法是省略它们的名称(如上所示)。我经常使用这个函数的一个用途是,当我需要在有条件编译的代码中命名函数参数时,比如在 assert 中。我发现以下代码比使用 #ifdef NDEBUG 更易读:

void rate(bool fantastic)
{
    assert(fantastic);
    ignore(fantastic);
}

12
自从C++17以后,你可以使用[[maybe_unused]]属性来忽略参数而不需要使用那些“创造性”的方式。 - Ruslan

2
可能的用途:
auto it = list_.before_begin();
for (auto& entry : list_)
{
    (void)entry; //suppress warning
    ++it;
}

现在迭代器 'it' 指向最后一个元素。

3
问题不是“何时有用”,而是“它是做什么的”。 - Ruslan
3
抑制警告 - Traummaennlein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接