在 C++0x 中,非静态数据成员初始值设定项是否会覆盖隐式复制构造函数?

发布于 2024-12-17 02:46:43 字数 1251 浏览 2 评论 0原文

根据 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 作为 Lion 的 Xcode 4.3 的一部分进行分发。

According to N2628 related to , non-static data member initializers can be overridden by explicitly defined constructors, but it appears to be slightly nebulous about the implicitly defined copy constructor.

In particular, I've noticed that with Apple clang version 3.0, the behavior varies depending on whether the struct (or class) is a POD.

The following program returns output "1", which indicates that the copy-constructor is ignoring the right-hand-side, and instead substituting the new non-static data member initializer (in this example, the boolean true value for 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;
}

However, confusingly, if you comment out string1:

    // std::string string1;  

then the behavior works as I expected (the output is "0"), presumably because there is no implicitly generated copy-constructor, and therefore the data is copied.

Does the C++0x specification really suggest that it is a good idea to allow the implicitly defined copy-constructor to not copy the contents of the right-hand side? Isn't that less useful and unintuitive? I find the non-static member initializer functionality to be quite convenient, but if this is the correct behavior, then I will explicitly avoid the feature due to its tricky and unintuitive behavior.

Please tell me I'm wrong?

UPDATE: This bug has been fixed in the Clang source repository. See this revision.

UPDATE: This bug appears fixed in Apple clang version 3.1 (tags/Apple/clang-318.0.45) (based on LLVM 3.1svn). This version of clang was distributed as part of Xcode 4.3 for Lion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

只是偏爱你 2024-12-24 02:46:43

它毕竟不是阴影,请参阅标准摘录的突出显示部分:

关于默认复制/移动构造函数的部分(第 12.8 节)有点太长,无法完整引用。底层是带有初始值设定项的非静态成员字段仍然简单地由默认的复制/移动构造函数

第 12.8 节复制:

-6。非联合类 X 的隐式定义的复制/移动构造函数执行成员复制/移动
其基地和成员。 [ 注意:非静态数据成员的大括号或等于初始化器将被忽略。看
也是 12.6.2 中的示例。 ——尾注]
初始化顺序与初始化顺序相同
用户定义的构造函数中的基数和成员(参见 12.6.2)。设 x 为以下参数之一
构造函数,或者对于移动构造函数,引用参数的 xvalue。每个基础或非静态数据
成员以适合其类型的方式复制/移动:

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

这是引用的示例:

<前><代码>结构A {
int i = /* 一些有副作用的整数表达式 */;
A(int arg) : i(arg) { }
// ...
};

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


为了完整起见,默认的默认构造函数的相应部分:

§ 12.1

-6。当odr-used(3.2)创建其类类型(1.8)的对象时,或者当它在第一个之后显式默认时,默认且未定义为删除的默认构造函数是隐式定义的。声明。
隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的该类的默认构造函数执行,没有构造函数初始化程序 (12.6.2) 和空
复合语句
。如果用户编写的默认构造函数格式错误,则程序格式错误。
如果用户编写的默认构造函数满足 constexpr 构造函数 (7.1.5) 的要求,
隐式定义的默认构造函数是 constexpr。在默认的默认构造函数之前
类是隐式定义的,其基类及其非静态的所有非用户提供的默认构造函数
数据成员应已隐式定义。 [ 注意:隐式声明的默认构造函数
有一个例外规范(15.4)。
显式默认定义可能具有隐式异常规范,
见 8.4。 —尾注]

It isn't shadowy after all, see highlighted parts of the standards excerpt:

The section on defaulted copy/move constructors (§ 12.8) is a bit too lengthy to quote in it's entirety. The low-down is that non-static member fields with initializers are still simply copied by the defaulted copy/move constructor

§ 12.8:

-6. The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ]
The order of initialization is the same as the order of initialization
of bases and members in a user-defined constructor (see 12.6.2). Let x be either the parameter of the
constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data
member is copied/moved in the manner appropriate to its type:

  • if the member is an array, each element is direct-initialized with the corresponding subobject of x;
  • if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
  • otherwise, the base or member is direct-initialized with the corresponding base or member of x.
    Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor

This is the sample referred to:

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

The A(int) constructor will simply initialize i to the value of arg, and the side effects in i’s brace-or-equalinitializer will not take place. —end example ]


For completeness, the corresponding section on the defaulted default constructor:

§ 12.1

-6. A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration.
The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty
compound-statement
. If that user-written default constructor would be ill-formed, the program is ill-formed.
If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5),
the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a
class is implicitly defined, all the non-user-provided default constructors for its base classes and its nonstatic
data members
shall have been implicitly defined. [ Note: An implicitly-declared default constructor
has an exception-specification (15.4).
An explicitly-defaulted definition might have an implicit exception-specification,
see 8.4. —end note ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文