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();
?
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 temporaryX
(i.e. the copy constructor is called "explicitly", soexplicit
is fine). In C++17 and newer, there's no temporaryX
here in the first place.