14

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

特别是,我注意到在 Apple clang 3.0 版中,行为会根据结构(或类)是否为 POD 而有所不同。

以下程序返回输出“1”,这表明复制构造函数忽略右侧,而是替换新的非静态数据成员初始值设定项(在本例中,X::a 的布尔真值)。

#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 的一部分分发。

4

1 回答 1

10

毕竟它并不模糊,请参阅标准摘录中突出显示的部分:

关于默认复制/移动构造函数的部分(第 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 或者是构造函数的参数,或者对于移动构造函数,是一个引用参数的 xvalue。每个基本或非静态数据成员都以适合其类型的方式复制/移动:

  • 如果该成员是一个数组,则每个元素都直接使用 x 的相应子对象进行初始化;
  • 如果成员 m 具有右值引用类型 T&&,则使用 static_cast(xm) 直接初始化它;
  • 否则,基数或成员将直接用 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。一个默认的且未定义为已删除的默认构造函数在被odr 使用(3.2) 创建其类类型 (1.8) 的对象时或在其第一次声明后显式默认时被隐式定义。
隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的该类的默认构造函数执行,没有 ctor-initializer (12.6.2) 和空的复合语句。如果该用户编写的默认构造函数格式不正确,则程序格式错误。
如果该用户编写的默认构造函数满足 constexpr 构造函数 (7.1.5) 的要求,则隐式定义的默认构造函数是 constexpr。在隐式定义类的默认默认构造函数之前,应隐式定义其基类及其非静态数据成员的所有非用户提供的默认构造函数。[注意:隐式声明的默认构造函数具有异常规范(15.4)。
一个显式默认的定义可能有一个隐含的异常规范,见 8.4。—end note ]

于 2011-11-21T20:58:40.787 回答