goo*_*era 14 c++ clang c++11 clang++
请考虑以下代码:
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";
}
Run Code Online (Sandbox Code Playgroud)
在clang ++中用-std = c ++ 11编译,p->i结果为零,但p->a.i没有.只要它的类没有用户提供的构造函数,整个对象是否应该归零?
编辑:由于评论中有一些广泛的讨论,我认为最好从标准中添加一些摘录:
对值初始化类型的对象
T意味着:
- 如果
T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);- 如果
T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则该对象是零初始化的,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数.- 如果
T是数组类型,则每个元素都是值初始化的;- 否则,该对象被零初始化.
零初始化T类型的对象或引用意味着:
- 如果
T是标量类型(3.9),则将对象设置为值0(零),作为整数常量表达式,转换为T;- 如果
T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;- if
T是一个(可能是cv限定的)联合类型,该对象的第一个非静态命名数据成员被零初始化,并且填充被初始化为零位;- if
T是数组类型,每个元素都是零初始化的;- 如果
T是引用类型,则不执行初始化.
每个的第二个子弹都适用于此.
Ric*_*ith 13
根据C++ 11标准和相关DR,Clang是正确的
在最初的C++ 11规范中,B{}将执行值初始化,从而导致a.i零初始化.对于像C++ 98这样的情况,这是行为的改变
B b = {};
Run Code Online (Sandbox Code Playgroud)
...在C++ 98中作为聚合初始化处理,但在C++ 11 FDIS中作为值初始化处理.
但是,在这种情况下的行为由核心问题1301改变,核心问题1301通过强制每当聚合由braced-init-list初始化时使用聚合初始化来恢复C++ 98行为.由于此问题被视为DR,因此它被视为事实上适用于早期版本的C++标准,因此符合要求的C++ 11编译器应在此处执行聚合初始化而不是值初始化.
最终,依靠值初始化来初始化数据成员是一个坏主意,尤其是对于具有用户提供的构造函数的类.
它确实看起来像一个错误(或者,如评论中所指出的,尽管指定了C++ 11,但仍按照C++ 03行事).在C++ 11中,值初始化应该a在调用其默认构造函数之前将成员归零.初始化B受8.5/7规则的约束
如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
按照a8.5/5的规则,零初始化应递归零初始化
如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的
当然,零初始化a应该设置i为零.
它不是编译器错误,它是代码中的错误.编译器似乎正在实现C++ 03行为,但这在C++ 11中发生了重大变化.
这些是C++ 03和C++ 11标准中的一些相关引用
在C++ 03中:
对值类型T的对象进行值初始化意味着:
- 如果T是具有用户声明的构造函数(12.1)的类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
- 如果T是没有用户声明的构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都是值初始化的;
(强调我的)
在C++ 11中:
对值类型T的对象进行值初始化意味着:
- 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
- 如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
和
零初始化T类型的对象或引用意味着:
- 如果T是标量类型(3.9),则将对象设置为值0(零),作为整数常量表达式,转换为T;
- 如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;
注意:以下仅适用于C++ 03:
删除A用户提供的构造函数,或将其更改为
A() : i() {}
Run Code Online (Sandbox Code Playgroud)
当你在这里初始化一个时B,
B* p = new B {};
Run Code Online (Sandbox Code Playgroud)
它值初始化其数据成员.由于A具有默认构造函数,因此值初始化会导致对该调用的调用.但是该构造函数没有显式初始化A::i,因此它被默认初始化,这int意味着不执行初始化.
如果您没有为其提供默认构造函数A,那么当A值初始化时,数据成员将被初始化为零.