views:

423

answers:

8
class A
{
 public:
  A(const int n_);
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ cout << "A::operator=(const A&)" << endl; }

int foo(const A& a_)
{ return 20; }

int main()
{
  A a(foo(A(10)));    // This is line 38
  return 0;
}

Executing this code gives o/p:
A::A(int), n_=10
A::A(int), n_=20

Apparently the copy constructor is never called.

class A
{
 public:
  A(const int n_);
  A& operator=(const A& that_);
 private:
  A(const A& that_);
};

However, if we make it private, this compile error occurs:
Test.cpp: In function ‘int main()’:
Test.cpp:21: error: ‘A::A(const A&)’ is private
Test.cpp:38: error: within this context

Why does the compiler complain when it doesn't actually use the copy constructor ?
I am using gcc version 4.1.2 20070925 (Red Hat 4.1.2-33)

+1  A: 

The copy-constructor isn't used, but in order for the code to compile the copy-constructor need to be accessible.

EDIT: Comeau C++ compiler reports the following:

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required
          for copy that was eliminated, is inaccessible
    A a(foo(A(10)));    // This is line 38
            ^

1 error detected in the compilation of "ComeauTest.c".

Note that if C++0x extensions are enabled, it compiles fine in Comeau C++ compiler.

dalle
What the heck does Comeau need to copy the temporary A object for? It's passed by reference!
xtofl
And what Return Value Optimization are you referring to?
xtofl
No A object is being returned so RVO is irrelevant to the copy constructor use. A(10) is an rvalue of type A, though, and the compiler may choose to take a copy of this to bind the reference parameter to. It's this potential copy that requires the copy constructors to be accessible.
Charles Bailey
+3  A: 

As far I see you are not using the copy constructor anywhere. In the statement foo(A(10)) you are creating a temporary object of class A and passing it as a const-reference to foo. The foo returns an integer which is used in the construction of object a. Hence I don't see where the copy constructor is getting involved here and how NRVO comes into picture. Also, I compiled the following code by making the copy constructor private and it compiled fine in VS2008.

using namespace std;

class A
{
 public:
  A(const int n_);
 private:
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ 
    cout << "A::operator=(const A&)" << endl; 
    return *this;
}

int foo(const A& a_)
{ return 20; }


int main(int argc,char *argv[])
{
   A a(foo(A(10)));    // This is line 38
  return 0;

}
Naveen
Same on VS2005, by the way.
xtofl
@AngryWhenHungry: BTW, you were missing return *this in the assignment operator. I hope that is not the compiler error you were getting :-)
Naveen
Check out James Hopkin's answer: http://stackoverflow.com/questions/919701/c-why-is-the-copy-ctor-used-in-this-code/920118#920118. Basically, binding a temporary to a const reference *still* requires the copy ctor (in C++ 03).
Richard Corden
A: 

In general, you shouldn't get to worried about if and when the copy constructor gets called. The C++ Standard is pretty relaxed about when calls to the copy constructor will be removed, or for that matter added. If your class logically needs it, provide it (and don't forget the destructor and assignment operator) is the sensible rule.

anon
+5  A: 

The 2003 standard, in §12.2.1, states:

Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11), shall be satisfied. ]

There are similar examples around. From what I gather, the compiler is free to generate temporaries or optimize them away.

aib
xtofl
+2  A: 

Just another remark: the compiler does a different thing when working with a temporary. So it's not about the copy constructor, it's about the intermediate temporary.

A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor
xtofl
+12  A: 

Core defect 391 explains the issue.

Basically, the current C++ standard requires a copy constructor to be available when passing a temporary of class type to a const reference.

This requirement will be removed in C++0x.

The logic behind requiring a copy constructor comes from this case:

C f();
const C& r = f(); // a copy is generated for r to refer to
James Hopkin
A: 

When calling:

foo( A(10) );

a temporary object is being created during the lifetime of the call. A copy constructor is being being used to populate the data. The temporary object is removed after execution of the call.

When calling:

{ A original(10); foo( original ); }

The original is being discarded after exiting the block. It can safely be used as a parameter.

For optimal speed, pass the object by reference, using a temporary variable that will be discarded by the compiler during its optimization.

ydebilloez
+1  A: 

In the expression:

A a(foo(A(10)));

The result of the sub-expression A(10) is an rvalue of type A. (5.2.3 [expr.type.conv])

When initializing a const reference from an rvalue the compiler may create a temporary from the rvalue and bind that to the reference. Even if it chooses not to, the copy constructor must be accessible. (8.5.3 [decl.init.ref]) This would not be the case if there reference were being initialized from a reference-compatible lvalue where direct binding is mandated.

As foo takes its parameter by reference and not value, there is no copy mandated for the argument initialization itself.

foo returns an int, so there is no copy of an A here.

a is direct initialized from the int returned by foo, so there is no copy of A here.

Charles Bailey