Explicit copy constructors disallow something like Foo foo = bar;
, and enforce the copy usage as Foo foo(bar);
. In addition, explicit copy constructors also disallow returning objects by value from a function. However, I tried replacing the copy initialization with braces, like so
struct Foo
{
Foo() = default;
explicit Foo(const Foo&) = default;
};
int main()
{
Foo bar;
Foo foo{bar}; // error here
}
and I am getting the error (g++5.2)
error: no matching function for call to 'Foo::Foo(Foo&)'
or (clang++)
error: excess elements in struct initializer
Removing the explicit
makes the code compilable under g++, clang++ still fails with the same error (thanks @Steephen). What's going on here? Is the uniform initialization considered as an initializer-list constructor (which trumps all others)? But if that's the case, why does the program compile when the copy constructor is non-explicit?
Uniform Initialization in C++ The uniform initialization is a feature that permits the usage of a consistent syntax to initialize variables and objects which are ranging from primitive type to aggregates. In other words, it introduces brace-initialization that applies braces ({}) to enclose initializer values.
Copy initialization is performed in the following situations: 1) when a named variable (automatic, static, or thread-local) of a non-reference type T is declared with the initializer consisting of an equals sign followed by an expression.
Constructor: It is a method which has the same name as the class which is used to create an instance of the class. Copy Constructor: Used to create an object by copying variables from another object of the same class.
An implicitly defined copy constructor will copy the bases and members of an object in the same order that a constructor would initialize the bases and members of the object.
You've encountered a case that was addressed by the resolution of Core issue 1467 immediately after C++14 was finalized.
Let's first note that class foo
is an aggregate. Your code is doing direct-list-initialization for foo
. The rules for list-initialization are in [8.5.4p3].
In C++14 (quoting from N4140, the working draft closest to the published standard), the paragraph above started with:
List-initialization of an object or reference of type
T
is defined as follows:
- If
T
is an aggregate, aggregate initialization is performed (8.5.1).[...]
So, if your class is an aggregate, the compiler tries to do aggregate initialization, which fails.
This was recognized as a problem, and fixed in the working draft. Quoting from the current version, N4527, the above-mentioned paragraph now starts with:
List-initialization of an object or reference of type
T
is defined as follows:
- If
T
is a class type and the initializer list has a single element of type cvU
, whereU
isT
or a class derived fromT
, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).- Otherwise, if
T
is a character array and the initializer list has a single element that is an appropriately typed string literal (8.5.2), initialization is performed as described in that section.- Otherwise, if
T
is an aggregate, aggregate initialization is performed (8.5.1).[...]
Your example now falls within the case described by the first bullet point, and foo
is direct-list-initialized using the defaulted copy constructor (regardless of whether it's explicit
, since it's direct initialization).
That is... if the compiler implements the resolution in the defect report.
explicit
.explicit
doesn't matter).bar
- looks like the EDG compiler used by IntelliSense wasn't updated either).Update: Since this answer was written, the working draft has been further amended in a couple of ways that are relevant to the example in the question and the explanation above:
- If
T
is an aggregate class and [...]
explicit
constructor (even defaulted) are no longer aggregates.This doesn't change the fact that, after all the changes are implemented, the example in the question is intended to work, with or without the explicit
; it's just worth knowing that the underlying mechanisms that make it work have changed slightly.
Note that all these changes are resolutions of defect reports, so they're supposed to apply when compilers are in C++14 and C++11 modes as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With