I stumbled across a strange oddity regarding copy constructors, and I was
hoping someone could explain where I'm going wrong. I tried searching google,
and looking through documentation, but couldn't find anything (or I'm blind).
I combined my test cases in http://www.stack.nl/~martijnb/constructortest.cpp
but will summarize below.
Consider a class CBar. Constructor, copy constructor and destructor have
all been implemented. As expected,
CBar a;
CBar *b = new CBar(a);
results in a call to the default constructor and the copy constructor.
However:
CBar *b = new CBar(CBar())
does *not*. Instead, it only calls the default constructor. Even more
interesting, the line above *doesn't compile* if I make CBar's copy
constructor private.
Can anyone explain me why GCC insists the copy constructor exists, but
doesn't bother calling it afterwards? I'm using GCC 4.2.1.
Kind regards,
Martijn
--
Martijn van Buul - pi...@dohd.org
I'll answer myself: 12.8.15
Crap.
"Martijn van Buul" <pi...@dohd.org> wrote in message
news:slrnhem2e...@mud.stack.nl...
Well.. to be honest, can you explain WHY then?
I don't understand either!
Bas
Take this program:
#include <iostream>
using namespace std;
class foo
{
public:
foo() { cout << "foo::foo()" << endl; }
~foo() { cout << "foo::~foo()" << endl; }
foo(foo const& ) { cout << "foo::foo(foo const& )" << endl; }
foo& operator= (foo ) { cout << "foo& foo::operator= (foo )" <<
endl; return *this; }
};
foo returnByVal()
{
foo x;
return x;
}
int main()
{
foo x = returnByVal();
}
This may have as little as one constructor call and one destructor
call. It may have as many as one default constructor call, two copy
constructor calls, and three destructor calls.
In cases where an object is copy constructed from a temporary, the
compiler is allowed to remove the temporary, and initialize the object
using the same arguments that were being used to construct the
temporary.
In cases where a function returns by value, the compiler is allowed to
remove the object in the called function's code which will be
returned, and instead directly construct the temporary in the caller's
code.
These two allowances, when combined, allow a compiler to produce
efficient return-by-value. It also means you might not be making as
many copies as you expect. Thus, if you have callable copy
constructors, your copies better be "equivalent".
Interesting results from the real world:
gcc version 4.1.2 20070626
With "g++ foo.cpp", "g++ -O0 foo.cpp", and "g++ -O3 foo.cpp", the
output of the program is
foo::foo()
foo::~foo()
gcc always optimizes cases under these allowances. How odd... That
might make debugging interesting.
Visual Studios 2008, "standard debug options"
foo::foo()
foo::foo(foo const& )
foo::~foo()
foo::~foo()
"standard release options"
foo::foo()
foo::~foo()
Sorry, let me emphasize that even if removing the copy constructor
would change visible aspects of the program, the compiler is allowed
to do this. This is not an exception under the "as if" rule. They're
two explicit exceptions, not governed by the "as if" rule, which can
change visible aspects of your program.
If copy constructor is not used?? Why cant we make it private??
It might help in the future if you use grammatically correct, whole
sentences.
I can only guess you mean to ask "If the compiler will optimize out
the copy constructor, then the copy constructor will not be used. In
this situation, my compiler complains that the copy constructor is not
accessible even though it will not, in fact, be used. Why is this?"
Formal answer: Because the standard says so. The explicit exception
which allows the compiler to remove temporaries and copy constructor
calls specifically says that the compiler must still ensure the the
copy constructor exists and is accessible.
I guess a better answer is that the people who wrote the standard did
not want some piece of code valid on one compiler which elided
temporaries and copy constructors, and not valid on another compiler
which did not do this optimization. They preferred that the code would
either fail on both compilers or pass on both compilers. You know,
basically help the portability of code.
However, since the outcome on different compilers can now be different should
your copy constructor have side effects (or if your copy constructor doesn't
"copy" at all!), portability is down the drain anyway.
--
Martijn van Buul - pi...@dohd.org
Yes, but if I make the copy constructor private to disable copying, I
would be really annoyed if my compiler copies it anyway.
Bo Persson
> I can only guess you mean to ask "If the compiler will optimize out
> the copy constructor, then the copy constructor will not be used. In
> this situation, my compiler complains that the copy constructor is not
> accessible even though it will not, in fact, be used. Why is this?"
>
> Formal answer: Because the standard says so. The explicit exception
> which allows the compiler to remove temporaries and copy constructor
> calls specifically says that the compiler must still ensure the the
> copy constructor exists and is accessible.
>
> I guess a better answer is that the people who wrote the standard did
> not want some piece of code valid on one compiler which elided
> temporaries and copy constructors, and not valid on another compiler
> which did not do this optimization. They preferred that the code would
> either fail on both compilers or pass on both compilers. You know,
> basically help the portability of code.
To put it short: The correctness of code does not depend on the optimization
behavior of your compiler, and so, incorrect code must be marked as such,
even if it would be optimized away later.
I would say that in a sane program, it doesn't really matter if the object
is copied or not.
So essentially if your implement a class such that:
Thing a(23);
Thing b = a;
Thing c(a);
Thing d = Thing(23);
and that obvious conclusion:
a == b == c == d
is not true at all!
Let me say that I prefer not encountering such classes too often. I
also believe that the potential benefit of allowing the optimizer to
optimize away a redundant copy are IMO much greater that the potential
benefit to make code that is so counter intuitive.
OTOH, if I create a class for object that have an identity, I
typically make them non-copyable typically by making the copy
constructor and assignment operator private.
Person a(23);
Person b = a; // Compilation error: can't assign a person
Person c(a); // Compilation error, can't duplicate a person
Person d = Person(23); // Compilation error should happen too!
I don't particulary mind if the temporary object Thing(23) is not
created and the copy is ellided, the visible results are the same
(unless you have a stupid cctor), but I certainly mind if the compiler
copies a non-copyable object even if that object was a temporary.
So IMO the stadard is perfectly correct.
Yannick