根据与N2628相关的c++0x,非静态数据成员初始值设定项可以被显式定义的构造函数覆盖,但它似乎对隐式定义的复制构造函数略显模糊。
特别是,我注意到使用Apple clang 3.0版时,行为会因结构(或类)是否为POD而有所不同。
以下程序返回输出“1”,表示复制构造函数忽略右侧,而是替换新的非静态数据成员初始值设定项(在此示例中,X的布尔值为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源存储库中修复。请参阅此revision。
更新:此错误在Apple clang 3.1版(标签/ Apple / clang-318.0.45)中出现(基于LLVM 3.1svn)。这个版本的clang是作为Lion的Xcode 4.3的一部分发布的。
答案 0 :(得分: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&amp;&amp;,则使用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的brace-or-equalinitializer中的副作用。
—end example ]
为完整起见,默认默认构造函数的相应部分:
§12.1
-6。当 odr-used (3.2)创建其类类型的对象(1.8)或者在其第一次显式默认后,默认构造函数被默认定义为未删除的默认构造函数。声明。
隐式定义的默认构造函数执行该类的初始化集合,该类集合将由该用户编写的默认构造函数执行,该类没有ctor-initializer(12.6.2)且为空 化合物语句即可。如果用户编写的默认构造函数不正确,则程序格式不正确 如果该用户编写的默认构造函数满足constexpr构造函数(7.1.5)的要求, 隐式定义的默认构造函数是constexpr。在默认的默认构造函数之前 class是隐式定义的,所有非用户提供的基类及其非静态的默认构造函数 数据成员应该是隐式定义的。 [注意:隐式声明的默认构造函数 有一个例外规范(15.4)。
明确默认的定义可能具有隐式异常规范, 见8.4。—end note ]