在c++ 0x中,非静态数据成员初始化式是否覆盖隐式复制构造函数?

In C++0x, do non-static data member initializers override the implicit copy constructor?

本文关键字:覆盖 是否 复制 构造函数 初始化 数据成员 0x c++ 静态      更新时间:2023-10-16

根据与c++0x相关的N2628,非静态数据成员初始化式可以被显式定义的构造函数覆盖,但是对于隐式定义的复制构造函数似乎有点模糊。

特别是,我注意到在Apple clang 3.0版本中,行为取决于结构(或类)是否是POD。

下面的程序返回输出"1",这表明复制构造函数忽略了右侧,而是替换了新的非静态数据成员初始化项(在本例中,为X::a使用布尔值true)。

#include <iostream>
#include <string>
struct X
{
    std::string string1;
    bool a = true;
};
int main(int argc, char *argv[])
{
    X x;
    x.a = false;
    X y(x);
    std::cout << y.a << std::endl;
}

然而,令人困惑的是,如果注释掉string1:

    // std::string string1;  

则行为如我预期的那样工作(输出为"0"),可能是因为没有隐式生成复制构造函数,因此数据被复制

c++ 0x规范真的建议允许隐式定义的复制构造函数而不是复制右侧的内容是一个好主意吗?这不是不那么有用和不直观吗?我发现非静态成员初始化功能非常方便,但如果这是正确的行为,那么我将显式地避免该功能,因为它的棘手和不直观的行为。

请告诉我我错了。

更新:Clang源代码库已经修复了这个错误。

更新:此错误在Apple clang版本3.1 (tags/Apple/clang-318.0.45)(基于LLVM 3.1svn)中得到修复。这个版本的clang是作为Xcode 4.3 for Lion的一部分发布的。

这毕竟不是阴影,参见标准摘录的高亮部分:

关于默认复制/移动构造函数的部分(§12.8)有点长,无法完整引用。实际上,带有初始化式的非静态成员字段仍然会被默认的复制/移动构造函数

复制。

§12.8:

6。非联合类X的隐式定义的复制/移动构造函数执行按成员复制/移动它的基础和成员。 [ Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. —end note ] 初始化顺序与初始化顺序相同用户定义的构造函数中的基和成员(参见12.6.2)。设x是的参数构造函数,或者对于move构造函数,引用参数的xvalue。每个基本或非静态数据以适合其类型的方式复制/移动成员:

  • 如果成员是数组,则使用x的对应子对象直接初始化每个元素;
  • 如果成员m具有右值引用类型T&&,则使用static_cast(x.m)直接初始化;
  • ,则用x的基或成员直接初始化相应的基或成员。
    虚基类的子对象只能由隐式定义的复制/移动构造函数
  • 初始化一次。

这是参考的示例:

struct A {
    int i = /* some integer expression with side effects */;
    A(int arg) : i(arg) { }
    // ...
};

A(int)构造函数将简单地将i初始化为arg的值,并且不会发生i的大括号或相等初始化式中的副作用。—end example ]


为完整起见,在默认的默认构造函数上对应的部分:

§12.1

6。当使用(3.2)创建其类类型(1.8)的对象时,或者当它在第一次声明后显式默认时,默认构造函数被隐式定义为默认且未定义为deleted。
隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的没有参数初始化式(12.6.2)且为空的类的默认构造函数执行复合语句。如果用户编写的默认构造函数是病态的,那么程序就是病态的。
如果用户编写的默认构造函数满足constexpr构造函数(7.1.5)的要求,隐式定义的默认构造函数是constexpr。的默认构造函数之前类是隐式定义的,它的基类及其非静态类的所有非用户提供的默认构造函数数据成员必须是隐式定义的。[注:隐式声明的默认构造函数。具有异常规范(15.4)。
显式默认定义可能具有隐式异常规范,见8.4。—end note ]