Meh*_*dad 15 c++ constructor initialization default-constructor visual-c++
我发誓我不记得以前见过这个,我很难相信自己的眼睛:
非聚合类的隐式定义的默认构造函数是初始化其成员还是否?
在Visual C++中,当我运行这个看似无辜的代码时......
#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }
Run Code Online (Sandbox Code Playgroud)
...令我惊讶的是,它返回一个非零值!但如果我删除字段b,则返回零.
我已经在VC++的所有版本上尝试了这个,我可以得到它,它似乎在所有这些上都这样做.
但是当我在Clang和GCC上尝试它时,无论我是在C++ 98模式还是C++ 11模式下尝试它,都会将值初始化为零.
什么是正确的行为?它不能保证为零吗?
小智 14
引用C++ 11:
5.2.3显式类型转换(功能表示法)[expr.type.conv]
2表达式
T(),其中是非数组完整对象类型或(可能是cv-qualified)类型T的simple-type-specifier或typename-specifiervoid,它创建一个指定类型的prvalue,它是值初始化的(8.5;没有为该void()情况进行初始化).[...]8.5初始值设定项[dcl.init]
7要初始化类型对象,
T意味着:
- ...
- 如果
T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则该对象是零初始化的,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数.- ...
所以在C++ 11中,S().a应该为零:在构造函数被调用之前,对象是零初始化的,并且构造函数永远不会将值更改a为其他任何值.
在C++ 11之前,值初始化具有不同的描述.引用N1577(大致是C++ 03):
对值类型T的对象进行值初始化意味着:
- ...
- 如果
T是没有用户声明的构造函数的非联合类类型,则每个非静态数据成员和基类组件T都是值初始化的;- ...
- 否则,该对象被零初始化
这里,值初始化S没有调用任何构造函数,而是导致其a和b成员的值初始化.a然后,该成员的值初始化导致该特定成员的零初始化.在C++ 03中,结果也保证为零.
甚至比这更早,转到第一个标准,C++ 98:
表达式
T(),其中是非数组完整对象类型T的简单类型说明符(7.1.5.2)或(可能是cv限定的)void类型,创建指定类型的右值,其值由默认初始化确定(8.5;没有对该void()情况进行初始化).
到默认初始化的类型的对象
T是指:
- 如果
T是非POD类类型(第9节),T则调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);- ...
- 否则,对象的存储被零初始化.
所以基于第一个标准,VC++是正确的:当你添加一个std::string成员,S变成非POD类型,而非POD类型没有得到零初始化时,他们只是调用它们的构造函数.隐式生成的默认构造函数S不会初始化a成员.
因此,只需遵循标准的不同版本,所有编译器都可以说是正确的.
正如@Columbo在评论中报道的那样,VC++的更高版本确实会a根据更新版本的C++标准对成员进行初始化.
Col*_*mbo 11
(第一部分中的所有引用均来自N3337,C++ 11 FD,编辑修改)
我无法在rextester上重现VC++的行为.据推测,这个bug(见下文)已经在他们正在使用的版本中修复,但不在你的版本中 - @Drop报告最新版本VS 2013 Update 4未能通过断言 - 而VS 2015预览会通过它们.
只是为了避免误解:S确实是一个集合.[dcl.init.aggr]/1:
聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3) ).
但这无关紧要.
值初始化的语义很重要.[dcl.init]/11:
初始化器为空的括号集的对象,即
(),应进行值初始化.
[dcl.init]/8:
对值初始化类型的对象
T意味着:
- if
T是一个(可能是cv限定的)类类型(第9条),没有默认构造函数(12.1)或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的;- 如果
T是没有用户提供或删除的默认构造函数的(可能是cv限定的)类类型,则该对象被零初始化并且检查默认初始化的语义约束,并且如果T具有非平凡的默认构造函数,则对象是默认初始化的;- [..]
显然,无论是否b存在,这都是有效的S.所以至少在C++ 11中,两种情况a都应该为零.Clang和GCC显示正确的行为.
现在让我们来看看C++ 03 FD:
对值初始化类型的对象
T意味着:
- if
T是具有用户声明的构造函数(12.1)的类类型(第9节)[...]- 如果
T是没有用户声明的构造函数的非联合类类型,则每个非静态数据成员和基类组件T都是值初始化的;- 如果
T是数组类型,则每个元素都是值初始化的;- 否则,该对象被零初始化
也就是说,即使在C++ 03中([dcl.init]/11中的上述引用也存在于/ 7中),两种情况a都应如此0.
同样,GCC和Clang都是正确的-std = c ++ 03.
如hvd的回答所示,您的版本仅适用于C++ 98和C++ 98.
| 归档时间: |
|
| 查看次数: |
1636 次 |
| 最近记录: |