Reputation: 76745
A recent question got me wondering about explicit copy constructors. Here is a sample code that I tried compiling under Visual Studio 2005 :
struct A
{
A() {}
explicit A(const A &) {}
};
// #1 > Compilation error (expected behavior)
A retByValue()
{
return A();
}
// #2 > Compiles just fine, but why ?
void passByValue(A a)
{
}
int main()
{
A a;
A b(a); // #3 > explicit copy construction : OK (expected behavior)
A c = a; // #4 > implicit copy construction : KO (expected behavior)
// Added after multiple comments : not an error according to VS 2005.
passByValue(a);
return 0;
}
Now for the questions :
[EDIT] I just found a funny link on MSDN with the exact same situation, and a mysterious comment from the main function : "c is copied" (as if it was obvious). As pointed by Oli Charlesworth : gcc does not compile this code and I believe he's right not to.
Upvotes: 39
Views: 12458
Reputation: 31435
A practical use, prior to C++11, of making a copy constructor explicit is in the case where it is actually part of making a class non-copyable.
The danger is that, although you declare the copy constructor private and don't implement it, if you do accidentally copy one either in a friend or in the class itself, the compiler won't pick it up and you'll only get a hard-to-find link error.
Making it explicit as well cuts down the chance of this as the compiler may well pick up your unintentional copy and point to the actual line where you're doing it.
In C++11 (and 14) there is no need to do this when using the =delete
syntax as you would get a compiler error even if copying within the class itself or within a friend.
Upvotes: 4
Reputation: 77400
I believe the relevant sections of C++03 are §12.3.1 2:
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value-initialization (8.5).
and § 8.5 12:
The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form
T x = a;
The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization and is equivalent to the form
T x(a);
Calling passByValue(a)
involves copy-initialization, not direct-initialization, and thus should be an error, according to C++03 § 12.3.1 2.
Upvotes: 44
Reputation: 272467
To expand on @MSalters' answer (which is correct), if you were to add passByValue(a);
to your main()
function, the compiler would should complain about it.
Explicit copy constructors are for preventing exactly this, i.e. to prevent implicit copying of resources in function calls and so on (essentially it forces the user to pass-by-reference rather than pass-by-value).
Upvotes: 2
Reputation: 179787
The definition of passByValue
is OK, because there's no statement that copies an A object. In the definition of retByValue
there's of course a return statement that copies an A object.
Upvotes: 5