39

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 :

  • Is #2 allowed by the standard ? If it is, what is the relevant section describing this situation ?
  • Do you known of any practical use for an explicit copy constructor ?

[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.

roalz
  • 2,699
  • 3
  • 25
  • 42
icecrime
  • 74,451
  • 13
  • 99
  • 111
  • I don't think explicit copy constructors a good idea. Where did you read about them? – fredoverflow Nov 11 '10 at 11:09
  • 1
    This seems to be fixed in VC++2010 - it gives an error for the line `passByValue(a);`. – user200783 Dec 08 '10 at 06:42
  • If you had an additional constructor for `A` that is not explicit e.g. `A(const char*)`, then I think you could still call `passByValue` by passing something that uses that alternative constructor e.g. `passByValue("foo")`. But I'm not quite confident enough to post this as an actual answer. – Arthur Tacca Jul 22 '20 at 14:23

4 Answers4

43

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.

outis
  • 75,655
  • 22
  • 151
  • 221
  • 2
    Although MSalters and Oli answers are correct, I'll accept this one because it makes it clear that VS is wrong. Thanks ! – icecrime Nov 11 '10 at 12:15
5

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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 1
    I'm not sure I understand your answer. Just to be clear : I kept the code to a minimum, but it's ok for the compiler to call `passByValue(A())` from the main, which I believe requires implicit copying. – icecrime Nov 11 '10 at 10:55
  • My understanding of this is that the `a` object is never used within `passByValue()`, so the compiler ignores it. – djeidot Nov 11 '10 at 11:27
  • @djeidot: I believe the call of `passByValue` still requires the copy constructor (and destructor) to be invoked. The compiler is free to emit these calls in certain situations for return values (although that can change the meaning of a program for non-trivial copy constructors!), but I am not aware of other places with this freedom. – Christopher Creutzig Nov 11 '10 at 12:22
  • The code implies that the object is copied to a temporary (which becomes the function parameter), and this is an error because the copy constructor is explicit. If the copy is redundant in the context (e.g. compiler may notice that the value isn't used by the function, and eliminate the copy) doesn't mean it's not an error to imply the use of the copy constructor. Though such an observation is certainly helpful in characterising the compiler bug. – greggo Sep 06 '16 at 16:36
4

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.

CashCow
  • 30,981
  • 5
  • 61
  • 92
2

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).

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • No, it won't, passByValue(a); in main() compiles fine, and I, as icecrime, believe that there's an implicit copying of the object. – davidnr Nov 11 '10 at 10:57
  • @davidnr, hmm, fails in gcc due to the reason mentioned above. – Nim Nov 11 '10 at 10:59
  • @davidnr: Under GCC, I get `error: no matching function for call to 'A::A(A&)', `error: initializing argument 1 of 'void passByValue(A)'` – Oliver Charlesworth Nov 11 '10 at 10:59
  • The compiler doesn't. I'll add it to my original post to make it clearer. Also, my question specifically concerns explicit copy ctor, I'm well aware of the use and benefits of the explicit keyword. – icecrime Nov 11 '10 at 10:59
  • @icecrime: Sorry, I didn't see you were asking specifically about explicit *copy* constructors... – Oliver Charlesworth Nov 11 '10 at 11:00
  • The question is about Visual C++ 2005 (well, 2008 in my case), and whether it's standard behaviour or not. It looks like VC doesn't honor the standard in this case. – davidnr Nov 11 '10 at 11:01
  • @davidnr: I don't have access to VC++ right now, but I'm pretty sure it's being too lax here (i.e. I believe GCC to be correct). Wikipedia appears to agree with me: http://en.wikipedia.org/wiki/Copy_constructor#Explicit_copy_constructor – Oliver Charlesworth Nov 11 '10 at 11:03