Consider this code:
struct X{
explicit X(){}
explicit X(const X&){}
};
void foo(X a = X()){}
int main(){}
Using C++14 standard, both GCC 7.1 and clang 4.0 rejects the code, which is what I expected.
However, using C++17 (-std=c++1z
), they both accept the code. What rule changed?
For both compilers to exhibit this same behavior, I doubt this to be a bug. But as far as I can tell, the latest draft still says, default argument uses the semantics of copy-initialization 1. Again, we know that explicit
constructors will only allow direct initialization 2.
1: dcl.fct.default/5; 2: class.conv.ctor/2
The constructors should be used to initialize member variables of the class because member variables cannot be declared or defined in a single statement. Therefore, constructors are used in initializing data members of a class when an object is created.
What is a Copy Constructor in C++? Copy constructors are the member functions of a class that initialize the data members of the class using another object of the same class. It copies the values of the data variables of one object of a class to the data members of 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.
A copy constructor is a member function that initializes an object using another object of the same class.
Because the behavior of copy elision changes from C++17; for this case copy elision is mandatory.
Mandatory elision of copy/move operations
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
T f() { return T(); } T x = T(T(f())); // only one call to default constructor of T, to initialize x
Note: the rule above does not specify an optimization: C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from. Another way to describe C++17 mechanics is "unmaterialized value passing": prvalues are returned and used without ever materializing a temporary.
And for copy initialization:
The effects of copy initialization are:
First, if
T
is a class type and the initializer is a prvalue expression whose cv-unqualified type is the same class asT
, the initializer expression itself, rather that a temporary materialized from it, is used to initialize the destination object: see copy elision (since C++17)If
T
is a class type and the cv-unqualified version of the type of other isT
or a class derived fromT
, the non-explicit constructors ofT
are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.
That means for X a = X()
, a
will be default constructed directly, the copy/move constructors and their side effects will be omiited completely. The selection of non-explicit constructors for overload resolution won't take place, which is required in C++14 (and before). For these guaranteed cases, the copy/move constructors don't participate in, then it won't matter whether they're explicit
or not.
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