会员没有归零,一个clang ++ bug?

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编译器应在此处执行聚合初始化而不是值初始化.

最终,依靠值初始化来初始化数据成员是一个坏主意,尤其是对于具有用户提供的构造函数的类.


Mik*_*our 6

它确实看起来像一个错误(或者,如评论中所指出的,尽管指定了C++ 11,但仍按照C++ 03行事).在C++ 11中,值初始化应该a在调用其默认构造函数之前将成员归零.初始化B受8.5/7规则的约束

如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.

按照a8.5/5的规则,零初始化应递归零初始化

如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的

当然,零初始化a应该设置i为零.

  • 知道了,这在C++ 11中有所改变.在C++ 03中,值初始化`如果T是非联合类类型而没有用户声明的构造函数,那么T的每个非静态数据成员和基类组件都是值初始化的; (3认同)
  • 看看Richard Smith的[答案](http://stackoverflow.com/a/23745872/365496)和他的参考资料.Clang似乎在相关DR下是正确的. (2认同)

jua*_*nza 5

不是编译器错误,它是代码中的错误.编译器似乎正在实现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值初始化时,数据成员将被初始化为零.