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

1

14

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.

Teammate answered 21/11, 2011 at 20:53 Comment(0)
C
10

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 ]

Crosspurpose answered 21/11, 2011 at 20:58 Comment(8)
Interesting. Is this in reference to the default copy-constructor?Teammate
This seems to only refer to the default constructor, AFAIK that is the constructor taking 0 arguments.Integrity
This reference talks about the default constructor not the copy constructorFuhrman
@DavidRodríguez-dribeas, @Kleist, @WillBradley Gee, peeps, are we the impatient crowd tonight :) AddedCrosspurpose
Thanks sehe. It looks like the behavior in Apple clang version 3.0 is a bug, then. For reference, I tried compiling this code in gcc version 4.6.1, and it gave back the following error, "test.cpp:7:11: sorry, unimplemented: non-static data member initializers"Teammate
@WillBradley: that's nice then; we're neither here nor there. Good thing: the standard is clear (even on the side-effects of any initializer expressions)Crosspurpose
@WillBradley if you haven't already, remember to file a bug llvm.org/bugsMembranous
@barnes53: I filed llvm.org/bugs/show_bug.cgi?id=11418 on this topic. Thanks for the link.Teammate

© 2022 - 2024 — McMap. All rights reserved.