Is copy constructor still involved in copy initialization?

287 Views Asked by At

Consider the following code:

class Y {};
class X
{
public:
    X() { }
    X(const Y&) { }
    explicit X(const X&) { }
};

X f()
{
    return X();
}

int main()
{
    Y y;
    X x = y;
    f();
}

The code gives an error (cannot convert from 'X' to 'X') at the line return X();. In my understanding, this is because X(X const&) is declared as explicit and X(X const&) is "called" implicitly to copy-initialize the returned object from the object created by X(). Since there is copy elision, X() will be used to create the returned object directly when X(const X&) is not explicit. However, the explicit keyword stops the code even if X(X const&) won't really be called, so my guess here is that the explicit keyword does not care whether there is copy elision or not.

In my understanding, a copy initialization in the form like X x = y; will first try to convert y to type X, and then copy that object of type X converted from y into x (so X x = y; does something like X x{ X{y} }), and since there is copy elision, the object of type X converted from y will directly be constructed as x.

Then, I comment out the definition and the call of the function f(). I am expecting the same error happened at return X(); happens to X x = y, because if X x = y does something like X x{ X{y} }, it would implicitly call explicit X(const X&) if there is no copy elision, and from my guess above the explicit keyword should not care whether there is copy elision or not.

But there is no compiler error this time. So, I'm guessing that X x = y would not call X(const X&) even if there is no copy elision. I'm guessing that X x = y is just an implicit call of X(const Y&).


May I ask if my guesses are correct? If not, could someone please tell me where I go wrong, and why explicit X(const X&) is not affecting X x = y; while it stops return X();?

1

There are 1 best solutions below

6
On

Your code compiles in C++17 in newer, since mandatory copy elision doesn't check for accessible copy/move constructors.

If you remove the function definition, the code is valid regardless of C++ version, because in X x = y;, x is direct-initialized from the temporary X (i.e. the copy constructor is called "explicitly", so explicit is fine). In C++17 and newer, there's no temporary X here in the first place.