请考虑以下代码:
class A { public: int i; A(){} }; B类{ public: A a; int i; }; int main(){ B * p = new B {}; std :: cout<< p - > i<< < p - > a.i< \\\; }在clang ++中编译-std = c ++ 11, p-> i 为零,但 p-> ai 不会。整个对象不应该被归零,只要它的类没有用户提供的构造函数?
编辑:因为在评论中有一些广泛的讨论,我认为最好从标准中添加一些摘录:
要初始化 T 表示:
- 如果 T 如果 T ,则初始化是不成立的。没有可访问的缺省构造函数);
- 如果 T 是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,零初始化,如果 T 的隐式声明的默认构造函数是非平凡的,那么构造函数被调用。
- 如果 T 是数组类型,则每个元素都被初始化;
要初始化对象引用类型T意味着:
- 如果 T 是标量类型,对象设置为值 0 (零),作为一个整数常数表达式,转换为 T ;
- 如果 T 是(可能是cv限定的)非联合类类型,每个非静态数据成员和每个base-类子对象被零初始化,并且填充被初始化为零位;
- 如果 T 是(可能是cv限定的)联合类型,对象的第一个非静态命名数据成员是零初始化并且填充被初始化为零比特;
- 如果 T 是数组类型,则每个元素都是初始化的;
- 如果 T 是参考类型,则不执行初始化。
在原来的C ++ 11规范中, B {} 将执行值初始化,导致 ai 为零初始化。对于
B b = {};...在C ++ 98中作为聚合初始化处理,但作为值初始化C ++ 11 FDIS。
但是,这种情况下的行为已更改核心问题1301 ,它通过强制执行聚合初始化来恢复C ++ 98行为,只要聚合由 braced-init-列表。由于这个问题被认为是一个DR,它被视为事实上应用于C ++标准的早期版本,因此一个合格的C ++ 11编译器应该在这里执行聚合初始化,而不是值 -
最终,依靠值初始化来初始化数据成员是一个坏主意,特别是对于具有用户提供的构造函数的类。
Consider the following code:
class A { public: int i; A() {} }; class B { public: A a; int i; }; int main() { B* p = new B {}; std::cout << p->i << " " << p->a.i << "\n"; }Compiled with -std=c++11 in clang++, p->i turns out to be zero, but p->a.i doesn't. Shouldn't the whole object be zeroed as long as its class doesn't have user-provided constructor?
EDIT: Since there are some extensive discussion in the comments, I think it's better to add some excerpt from the standard here:
To value-initialize an object of type T means:
- if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
- if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
- if T is an array type, then each element is value-initialized;
- otherwise, the object is zero-initialized.
To zero-initialize an object or reference of type T means:
- if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;
- if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
- if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero-initialized and padding is initialized to zero bits;
- if T is an array type, each element is zero-initialized;
- if T is a reference type, no initialization is performed.
The second bullet of each applies here.
解决方案Clang is correct, per the C++11 standard plus relevant DRs
In the original C++11 specification, B{} would perform value-initialization, resulting in a.i being zero-initialized. This was a change in behavior compared to C++98 for cases like
B b = {};... which were handled as aggregate initialization in C++98 but treated as value-initialization in C++11 FDIS.
However, the behavior in this case was changed by core issue 1301, which restored the C++98 behavior by mandating that aggregate initialization is used whenever an aggregate is initialized by a braced-init-list. Since this issue is considered a DR, it is treated as de facto applying to earlier revisions of the C++ standard, so a conforming C++11 compiler would be expected to perform aggregate initialization here rather than value-initialization.
Ultimately, it's a bad idea to rely on value-initialization to initialize your data members, especially for a class that has user-provided constructors.
更多推荐
成员不归零,一个clang ++错误?
发布评论