tags:

views:

253

answers:

6

Hi guys,

Can one of you explain why the following piece of code does not compile?

#include <iostream>

using namespace std;

class Foo
{
public:
  Foo() { cout << "Foo::Foo()" << endl << endl; }
  Foo& operator=(const Foo&) { cout << "Foo::operator=(const Foo&)" << endl << endl; }
private:
  Foo(const Foo& b) { *this = b; cout << "Foo::Foo(const Foo&)" << endl << endl; }
};

int main()
{
  Foo foo;

  foo = Foo();
}

The error I receive:

$ g++ -o copy_ctor_assign copy_ctor_assign.cc && ./copy_ctor_assign
copy_ctor_assign.cc: In function 'int main()':
copy_ctor_assign.cc:10: error: 'Foo::Foo(const Foo&)' is private
copy_ctor_assign.cc:17: error: within this context

Note: when I remove the private: keyword the code compiles but the copy ctor is never called. So why does it err when it's private?

Not sure if it's important but I'm using:

$ g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44)
Copyright (C) 2006 Free Software Foundation, Inc.
A: 

Copy Ctor is called when:

  1. passing an object by value as parameter to a function,
  2. returning an object from a function.

So you are certainly doing one or both of these case somewhere in your code. You should set Copy Ctor as public or avoid the 2 previous cases.

Patrice Bernassola
He did.Try to compile his code. The error is raised (without any additional code)
Oren S
A: 

Copy constructor would be called if you write

Foo foo; // normal constructor
Foo foo1(foo); //copy constructor

In your case, first the default constructor is called and then the operator= method.

Ashalynd
Then why does his code not compile?
sepp2k
Perhaps his code doesn't compile with Werror? ;)
UncleBens
+3  A: 

Assuming that the code you've posted is the only code in the project, and there's no covert passing of Foos by value going on anywhere, all I can figure is that gcc is optimizing

Foo foo;
foo = Foo();

to

Foo foo = Foo();

...which is unsound, as the first form is a default-construct and an assignment, while the second is equivalent to

Foo foo(Foo());

...which is clearly a copy construction. If I'm right, the copy constructor is not being run because GCC can optimize away the redundant temporary; this is permitted by the C++ spec.

In general, it is not a good idea to have assignment operators and copy constructors at different protection levels; as you've seen, the results can be unintuitive.

David Seiler
gcc can't optimize the constructors like that, since they have side effects (the `cout`s). If gcc does that, it's a bug.
CAdaker
@CAdaker: nope. Removing redundant temporaries is a special case and the C++ standard explicitly allows eliding copy construction under such circumstances.
Konrad Rudolph
Even when the temporaries have non-trivial constructors and destructors? That sounds completely insane.
CAdaker
Yeah, but it's still legal.
David Seiler
I tried to compile his code with -O0 (no optimization). same problem.And when the "public:" is removed the copy C'tor is not called at all.I can't figure it out but it doesn't look like an optimization. Otherwise the copy C'tor should have been called...
Oren S
Oops, sorry, I've been misreading you. Yes, it makes sense that the compiler can optimize away the copy operation, but getting rid of the assignment is going too far.
CAdaker
@CAdaker: Oh. Yes, I agree; if GCC is transforming the assignment to a copy, it's wrong. That it compiles under 4.3 and 4.4.1 suggests a regression in gcc, that the maintainers fixed. But I could be completely wrong.
David Seiler
@Oren: I'm positing two optimizations: replacing default-construct-and-assign with construct-temporary-and-initialize, and then replacing *that* with initialize-directly. But if it still happens under -O0, I don't know what to think.
David Seiler
GCC can optimize whatever it wants, but it can't violate the as-if rule. There's ansolutely no way it can suddenly strat requiring a copy constructor for the code which didn't require one under the "canonical" (non-optimized) interpretation.
AndreyT
I agree. As I said in my response, if GCC was doing that, it was a bug. That it happens in 4.1, but not in 4.3.3 or 4.4.1, is suggestive.
David Seiler
+4  A: 

That code compiles with gcc 4.3.3 and 4.4.1. Maybe that's just a bug in gcc 4.1?

CAdaker
Can you try to recompile in these versions using -Wall?Thanks
Oren S
I get warnings about operator= not returning a value, but otherwise nothing.
CAdaker
It fails for me: using g++ (GCC) 3.4.4
Martin York
Not a bug but required for c++03
Johannes Schaub - litb
A: 
#include <iostream>

using namespace std;

class Foo
{
public:
  Foo() { cout << "Foo::Foo()" << endl << endl; }
  Foo& operator=(const Foo&) { cout << "Foo::operator=(const Foo&)" << endl << endl; }
  Foo(const Foo& b) { *this = b; cout << "Foo::Foo(const Foo&)" << endl << endl; }
};

int main()
{
  Foo f1;// default constructor called

  Foo f2 = f1; //copy constructor called
}

Check this, in Foo f2=f1; ( f2 is created using copy constructor)

subbul
What are you trying to say in the above code? The question was asked why it is getting compilation error when the copy constructor is private. You just gave an example of default constructor and copy constructor.
Jagannath
+5  A: 

You are initializing a reference from temporary.
The standard states:
The temporary should be initialized (8.5.3 par 5)"using the rules for a non-reference copy initialization (8.5)".

The copy construction is removed for the temporary (permitted by the standard. 12.8 par 5).
However, the standard clearly states (12.2 par 1):
"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. ]"

(also, when looking for the right quote, found this duplicate :)

Edit: adding relevant location from the standard

Oren S
This answer is correct, but you quote the wrong part of `8.5.3 para 5` (in our case, we have class types and reference compatible types, so the *previous* bullets should be taken). The important one is the previous one and it says whether to copy or not is implementation defined and then "The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.". For C++0x, the implementation is not allowed to copy anymore, and will not require a copy constructor anymore.
Johannes Schaub - litb
I stand corrected :)thanks
Oren S
Thank you, I learned something new today.
sdumi