【问题标题】:Why doesn't this C++ expression generate even a warning?为什么这个 C++ 表达式甚至不生成警告?
【发布时间】:2014-10-12 19:02:02
【问题描述】:

我知道您可以禁止将表达式转换为无效的“表达式无效”警告:

int main()
{
    void(2+3);
}

如果我没记错的话,将某些东西转换为 void 的含义并不是将表达式转换为 void 类型的对象(void 是一个没有对象的类型,或者更好地说,是一个空集),但是告诉编译器我们要忽略表达式的值。其他相关示例:

int main()
{
    (void)(2+3);
}

这个样本和其他样本之间有什么语义差异吗?

最后:

int main()
{
    void();
}

不返回任何警告(这里是 Coliru test,充满了 pedantic.related 选项);但是,这会产生错误:

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

// Error:
//    main.cpp:6:9: error: expected primary-expression before 'void'
//       (void)();
//        ^
//    main.cpp:6:9: error: expected ')' before 'void'

void() 表达式的含义是什么?您是在创建一个没有意义的 void 类型的临时对象吗?还是将一个空表达式转换为 void?

总的来说,我想了解有关 void 使用的完整情况,以及标准指定的行为/语义。

【问题讨论】:

  • 请注意,标准(至少,不是在 C 中,不能确定 C++)不需要此诊断;这只是编译器作者有帮助。因此,您不会在标准中找到任何解决此问题的内容。
  • @OliverCharlesworth 谢谢。我会用你的评论更新我的问题。
  • " 但是,这会产生错误" - 问题中没有错误。如果 yiu 提到错误,请同时发布!
  • 事实上,(void)unused_arg 是关闭特定函数中未使用参数的警告的常用方法,如果您仍希望此类警告出现在其他地方。
  • 例如,gcc 表示-Wunused-valueTo suppress this warning cast the unused expression to ‘void’.

标签: c++ casting void


【解决方案1】:

这个void(2+3) 不是强制转换,它是一个伪构造函数。是的,void(2+3)(void)(2+3) 之间存在语义差异。 void(2+3) 是一个具有隐式转换的构造,例如:void((void)(2+3))void() 也是一个合法的伪构造,尽管它使用伪默认构造函数而不是伪复制构造函数。

是的void() 本质上是创建一个类型为void 的临时对象(尽管正如你所说,你实际上不能拥有 void 类型的对象,所以你永远不能用它做任何事情;它不能作为一个参数,分配给一个变量等)

强制转换对表达式进行操作:(void) (2+3) 对表达式 (2+3) 进行操作。但是() 不是合法的表达,所以(void)() 是不合法的。同样,你不能这样做 static_cast<void>()static_cast<void>(())

【讨论】:

  • @Peregring-lk 伪构造函数支持复制构造语法。例如。 int(1)。与常规复制构造函数一样,编译器隐式执行从您传递的任何内容到复制构造函数的参数类型的转换。例如。 int(10.0)10.0double 隐式转换为 int。伪构造函数void(2+3) 也是如此:编译器将隐式转换插入到void:void((void)(2+3))。当你使用语法void() 时,这不会使用伪复制构造函数,而是使用伪默认构造函数,因此没有隐式强制转换。
  • 我以前从未听说过“伪构造函数”这个词。 C++ 标准将此表达式称为“显式类型转换(功能表示法)”,并在 [expr.type.conv]/1 中说“A simple-type-specifiertypename-specifier 后跟一个带括号的 expression-list 构造一个给定表达式列表的指定类型的值。如果表达式列表是单个表达式,则类型转换表达式是等价的(在定义上,并且如果在含义中定义)到相应的强制转换表达式。”所以它演员表。
  • @Peregring-lk 这听起来很对,但关于默认构造函数的最后一部分并不是全部:在T() 的形式中,创建了一个prvalue(一个“临时”,但不是必须是一个对象),它是值初始化的。值初始化可以调用默认构造函数,但仅限于某些情况。即使对于类类型,如果默认构造函数很简单,也不会调用它进行值初始化。
  • @Peregring-lk 值已初始化。不,您不需要为值初始化传递值。正如 dyp 所说,T() 会产生一个纯右值,即使对于像 void() 这样的东西也是如此,所以这是一个非对象“临时”的例子。
  • @Peregring-lk "值初始化" 只是一个名称;) 零初始化不调用构造函数,因此 T() 不会正确构造复杂的类类型。但是,值初始化可以执行零初始化(例如,对于具有普通默认构造函数的基本类型和类类型)。
猜你喜欢
  • 2014-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
相关资源
最近更新 更多