In updating some code to use uniform initialization, I thought it would be a drop-in modern substitue for the now "old style" parentheses style. I know this isn't always the case (obvious example, vector<int>
) but I've stumbled over another difference that I don't understand.
class Object {
public:
Object() = default;
Object(const Object&) = default;
};
int main() {
Object o;
Object copy{o}; // error
Object copy2(o); // OK
}
fails to compile under clang3.5 with the error: (also fails under gcc)
error: excess elements in struct initializer
There are two different changes to Object
that make this work. Either adding a data member to it, or giving it an empty copy constructor body
class Object {
private:
int i; // this fixes it
public:
Object() = default;
Object(const Object&) { } // and/or this fixes it as well
};
I don't see why these should make a difference.
Johannes's answer is useful, but let me elaborate on why it currently happens.
Both of the changes you describe affect your class by making it go from an aggregate to not an aggregate. See C++11 (N3485) § 8.5.1/1:
A constructor with a definition of
= default
is considered not user-defined.Then, going down to list-initialization in § 8.5.4, we see:
And then a bunch of "Otherwise..." sections. Thus, changing either of these allows a constructor to be called instead of performing aggregate initialization.
The new proposed standardese for the list-initialization definition (as viewable in Johannes's link) provides a prioritized case of a single element in the list, and it having a type of (or really close to) the type of the object being initialized. Aggregate initialization would then be top priority after that.