views:

160

answers:

3

Hello, if I compile (under G++) and run the following code it prints "Foo::Foo(int)". However after making copy constructor and assignment operators private, it fails to compile with the following error: "error: ‘Foo::Foo(const Foo&)’ is private". How comes it needs a copy constructor if it only calls standard constructor at runtime?

#include <iostream>

using namespace std;

struct Foo {
    Foo(int x) {
        cout << __PRETTY_FUNCTION__ << endl;
    }


    Foo(const Foo& f) {
        cout << __PRETTY_FUNCTION__ << endl;
    }

    Foo& operator=(const Foo& f) {
        cout << __PRETTY_FUNCTION__ << endl;
        return *this;
    }
};

int main() {
    Foo f = Foo(3);
}
+15  A: 

The copy constructor is used here:

Foo f = Foo(3);

This is equivalent to:

Foo f( Foo(3) );

where the first set of parens a re a call to the copy constructor. You can avoid this by saying:

Foo f(3);

Note that the compiler may choose to optimise away the copy constructor call, but the copy constructor must still be available (i.e not private). The C++ Standard specifically allows this optimisation (see section 12.8/15), no matter what an implementation of the copy constructor actually does.

anon
If you claim it uses the copy constructor, why do you not see it in the output. The original poster clearly stated that only ::Foo(int) is called.
KIV
@Neil but the copy constructor clearly does something so it seems unlikely that the compiler would optimise it away...
Matthew Murdoch
@Matthew: No. The standard explicitly allows such calls to be optimized away so the compiler simply doesn't care about any side-effects. Any modern compiler worth its salt *will* optimize this call. On the other hand, the standard also clearly states that the call must still be possible. Thus, Neil's explanation is right and to the point.
Konrad Rudolph
@KIV, Matthew: Neil and Konrad are right, but don't have nightmares: the compiler is only allowed to optimise away the copy-ctor call **when constructing a temporary object**. Your copy-ctor will still be called if you explicitly request a copy, e.g. with "Foo f(3); Foo g(f);" or "Foo f(3); Foo g = f;".
j_random_hacker
+2  A: 

When you say Foo f = Foo(3), it's going to make a temporary Foo constructed from 3, then copy construct it into f.

You want:

Foo f(3);

You might also notice that even though you are saying =, it uses the copy constructor. The compiler will directly construct f from a copy of the temporary when using this syntax.

It will actually call operator= if you say something like:

Foo f;
f = Foo(3);
GMan
+2  A: 

What you see, is a result of allowed by standard optimization, when compiler avoids creation of temporary. Compiler is allowed to replace construction and assignment with simple construction even in presence of side effects (like IO in your example).

But fact if program is ill-formed or not should not depend on situation, when compiler makes this optimization or not. That's why

Foo f = Foo(3);

requires copy constructor. And

Foo f(3);

does not. Though it will probably lead to same binary code.

Quote from 12.8.15

When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.111) This elision of copy operations is permitted in the following circumstances (which may be combined to eliminate multiple copies):

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function’s return value

— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy

See also "Return value optimization".

Konstantin