C++ 默认构造函数是否总是初始化所有成员?

C++ 默认构造函数是否总是初始化所有成员?,c++,visual-c++,constructor,initialization,default-constructor,C++,Visual C++,Constructor,Initialization,Default Constructor,我发誓我不记得以前见过这个,我很难相信我的眼睛: 非聚合类的隐式定义的默认构造函数是否初始化其成员 在VisualC++中,当我运行这个无辜的代码…< /P> #include <string> struct S { int a; std::string b; }; int main() { return S().a; } #包括 结构S{inta;std::string b;}; int main(){return S().a;} 。。。令我惊讶的是,它返回了一个非零值!但如果

我发誓我不记得以前见过这个,我很难相信我的眼睛:

非聚合类的隐式定义的默认构造函数是否初始化其成员

在VisualC++中,当我运行这个无辜的代码…< /P>
#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }
#包括
结构S{inta;std::string b;};
int main(){return S().a;}
。。。令我惊讶的是,它返回了一个非零值!但如果我删除字段
b
,那么它将返回零

我已经在我能接触到的所有版本的VC++上试过了,而且似乎在所有版本上都能做到这一点

但是,当我在Clang和GCC上尝试时,无论我是在C++98模式还是在C++11模式下尝试,值都初始化为零

正确的行为是什么?不保证为零吗?

(第一节中的所有引用均来自N3337,C++11 FD,并进行了编辑性修改)

我不能用这种方法复制这种行为。大概这个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
类型的对象进行值初始化意味着:

  • 如果
    T
    是(可能是cv限定的)类类型(第9条),没有默认构造函数(12.1)或 用户提供或删除的默认构造函数,则对象默认初始化
  • 如果
    T
    是一个(可能是cv限定的)类类型,没有用户提供或删除的默认构造函数,则对象为零初始化
    并检查默认初始化的语义约束,如果
    T
    具有非平凡的默认构造函数,则对象为默认初始化对象
  • [……]
显然,无论
b
是否在
S
中,这都是有效的。因此,至少在C++11中,在这两种情况下,
a
都应该为零


现在让我们看看C++03 FD:

T
类型的对象进行值初始化意味着:

  • 如果
    T
    是一个类类型(第9条),且具有用户声明的构造函数(12.1)[..]
  • 如果
    T
    是没有用户声明构造函数的非联合类类型,则每个非静态数据成员和基类
    T
    的组件初始化为值
  • 如果
    T
    是数组类型,则每个元素都初始化了值
  • 否则,对象初始化为零
也就是说,即使在C++03中(其中[dcl.init]/11中的上述引用也存在于/7中),
a
在这两种情况下都应该是
0

同样,使用-std=c++03

如中所示,您的版本与C++98兼容,并且仅与C++98兼容。

引用C++11:

5.2.3显式类型转换(函数表示法)[expr.type.conv]

2表达式
T()
,其中
T
是非数组完整对象类型或(可能是cv限定的)
void
类型的简单类型说明符或类型名说明符,创建指定类型的PR值,该值已初始化(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
成员的值初始化。然后,该
成员的值初始化导致该特定成员的零初始化。在C++03中,结果也保证为零

甚至早于此,我们来看看最早的标准C++98:

表达式
T()
,其中
T
是非数组完整对象类型或(可能是cv限定的)
void
类型的简单类型说明符(7.1.5.2),创建指定类型的右值,其值由默认初始化确定(8.5;不对
void()
案例进行初始化)

默认情况下,初始化类型为
T
的对象意味着:

  • 如果
    T
    是非POD类类型(第9条),则调用
    T
    的默认构造函数(如果
    T
    没有可访问的数据,则初始化是错误的)