构建一个空对象

7
下面的代码示例似乎可以使用GCC 12.2编译,但与Clang 15和MSVC 19.33不同,由于错误illegal initializer type 'void'。我希望所有三个编译器都会拒绝该代码,因为构造(临时)void对象是没有意义的。
auto f() { return void{}; }

using t = decltype(f());

在这个例子中,函数f只与未求值的操作数一起使用。是否允许像这样使用f,还是应该拒绝其定义?(我知道合法表达式void()可以创建一个prvalue,但这不是本帖的重点。)
标准中似乎相关的段落:

[basic.types]/5

不完全定义的对象类型和cv空类型是不完整的类型([basic.fundamental])。

[basic.types]/11.43

一个不完全定义的对象类型的实例的大小和布局是未知的。


2
void{} 应该是有效的。https://dev59.com/c6_la4cB1Zd3GeqPxsHR?noredirect=1&lq=1 - apple apple
所有三个编译器都无法编译此代码 - 实时 - https://godbolt.org/z/W3qKT5d58 请检查编译标志等。 - Richard Critten
@appleapple "...它是一种无法完成的不完整类型(因此,禁止使用void类型的对象)。..." Void type - Richard Critten
1
@RichardCritten:你的GCC版本相当老了。 - Nicol Bolas
@RichardCritten 感谢您指出这一点,看来我测试的代码是使用 MSVC trunk 而不是 MSVC 19.33(后者也无法编译代码)。我已经相应地更新了帖子,因为由于 GCC 的结果,编译器差异仍然存在。 - 303
显示剩余3条评论
1个回答

4

来自§expr.type.conv

如果初始化器是一个括号内的单个表达式,则类型转换表达式等效于相应的强制类型转换表达式。否则,如果类型为cv void并且初始化器为(){}(在任何展开参数包后),则该表达式是指定类型的prvalue对象,不执行任何初始化。否则,该表达式是指定类型的prvalue,其结果对象使用初始化程序进行直接初始化。如果初始化程序是一个带有可选表达式列表的括号,指定的类型不应是数组类型。

因此,void{}等同于void(),可以得到有效的(cv)void prvalue。


我猜工作草案N4861是C++20标准的最接近之处。所指的[expr.type.conv]/2确实似乎与[basic.types]/5和脚注[basic.types]/11.39相矛盾。不确定如何处理这一切,是否应该将Clang 15和MSVC 19.33的观察行为报告为错误? - 303
由于最新的MSVC更改了行为,我认为19.33中的行为是一个错误。(根据我的标准阅读) - apple apple
@303 其实我不明白为什么 void 没有大小变化,参见 §expr.type.conv - apple apple
@303,正如你在问题中所述,void()已经是有效的,同样也适用于void{}。(而且clang确实接受void() - apple apple
1
@303 expr.type.conv 表示表达式的结果是一个(cv-qualified) void _prvalue_。它并没有说会创建一个 void 类型的对象(由于您引用的其他标准条款,这是不可能的)。创建一个对象需要在需要临时材料化转换的上下文中使用 prvalue 表达式,例如弃值表达式。但即使在那里,当前的草案也澄清了对于(cv-qualified) void prvalues不会发生这种情况(https://eel.is/c++draft/expr#context-2.sentence-6)。然而,C++20草案对此有些不太清楚。 - user17732522
1
@303 最新的 MSVC 已经接受了根据 DR 2351 的代码,而 clang 已经有一个开放的错误报告来实现它 这里 - user17732522

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