在 C++0x 中,非静态数据成员初始化器会覆盖隐式复制构造函数吗?

Posted

技术标签:

【中文标题】在 C++0x 中,非静态数据成员初始化器会覆盖隐式复制构造函数吗?【英文标题】:In C++0x, do non-static data member initializers override the implicit copy constructor? 【发布时间】:2011-11-21 20:53:00 【问题描述】:

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

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

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

#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 版 (tags/Apple/clang-318.0.45) 中已修复(基于 LLVM 3.1svn)。此版本的 clang 作为 Xcode 4.3 for Lion 的一部分分发。

【问题讨论】:

【参考方案1】:

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

关于默认复制/移动构造函数的部分(第 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(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。当 odr-used (3.2) 创建其类类型 (1.8) 的对象或在其第一次显式默认后,默认构造函数被默认且未定义为已删除的默认构造函数被隐式定义隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的该类的默认构造函数执行,没有 ctor-initializer (12.6.2) 和空 复合语句。如果该用户编写的默认构造函数格式不正确,则程序格式错误。 如果该用户编写的默认构造函数满足 constexpr 构造函数(7.1.5)的要求, 隐式定义的默认构造函数是 constexpr。在默认的默认构造函数之前 类是隐式定义的,其基类 及其非静态的所有非用户提供的默认构造函数 数据成员 应已被隐式定义。 [注意:隐式声明的默认构造函数 有一个异常规范(15.4)。 显式默认定义可能具有隐式异常规范, 见 8.4。 —end note ]

【讨论】:

有趣。这是参考默认的 copy-constructor 吗? @DavidRodríguez-dribeas、@Kleist、@WillBradley Gee,偷看,我们今晚是不是不耐烦的人群:) 已添加 谢谢。那么,Apple clang 3.0 版中的行为似乎是一个错误。作为参考,我尝试在 gcc 版本 4.6.1 中编译此代码,它返回以下错误,“test.cpp:7:11: sorry, unimplemented: non-static data member initializers” @WillBradley 如果你还没有,记得提交错误llvm.org/bugs @barnes53:我就这个话题提交了llvm.org/bugs/show_bug.cgi?id=11418。感谢您的链接。

以上是关于在 C++0x 中,非静态数据成员初始化器会覆盖隐式复制构造函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

c2797 未实现成员初始化器列表或非静态数据成员初始化器内的列表初始化

具有非静态成员初始化器的类的 C++11 聚合初始化

访问父类的成员“非静态数据成员的使用无效”C++

C ++初始化对象的非静态成员数组

final

对Final关键字的理解以及说明