Why isn't the copy constructor called
-
I feel I'm turning slightly mad. In my mind the two variants below should give the same output:
class A {
public:
A () { cout << "A created" << endl; }
};class B {
public:
B () { cout << "Default B constructor" << endl; }
B (A oa) { cout << "B created from A" << endl; }
B (const B& other) { cout << "B copy constructor" << endl; }
};
int main()
{
A aobj;
B* bptr = new B (B (aobj));cout << "--------" << endl;
B bobj (aobj);
B* bbptr = new B (bobj);
}After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:
A created
B created from AB created from A
B copy constructorWhat am I missing here?
Mircea
-
I feel I'm turning slightly mad. In my mind the two variants below should give the same output:
class A {
public:
A () { cout << "A created" << endl; }
};class B {
public:
B () { cout << "Default B constructor" << endl; }
B (A oa) { cout << "B created from A" << endl; }
B (const B& other) { cout << "B copy constructor" << endl; }
};
int main()
{
A aobj;
B* bptr = new B (B (aobj));cout << "--------" << endl;
B bobj (aobj);
B* bbptr = new B (bobj);
}After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:
A created
B created from AB created from A
B copy constructorWhat am I missing here?
Mircea
I might be wrong, but
B (aobj)
from
new B (B (aobj))
should call move constructor, not copy constructor ... P.S.
B (B (aobj))
would call a copy constructor only if what is inside of first brackets would be an already created B object, a l value, so, since
B (aobj)
is an anonymous object, move construct would be called. P.P.S. I added move constructor to B
B(B&& rhs) { std::cout << "B move constructor" << std::endl; }
and is never called either.
-
I feel I'm turning slightly mad. In my mind the two variants below should give the same output:
class A {
public:
A () { cout << "A created" << endl; }
};class B {
public:
B () { cout << "Default B constructor" << endl; }
B (A oa) { cout << "B created from A" << endl; }
B (const B& other) { cout << "B copy constructor" << endl; }
};
int main()
{
A aobj;
B* bptr = new B (B (aobj));cout << "--------" << endl;
B bobj (aobj);
B* bbptr = new B (bobj);
}After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:
A created
B created from AB created from A
B copy constructorWhat am I missing here?
Mircea
-
I might be wrong, but
B (aobj)
from
new B (B (aobj))
should call move constructor, not copy constructor ... P.S.
B (B (aobj))
would call a copy constructor only if what is inside of first brackets would be an already created B object, a l value, so, since
B (aobj)
is an anonymous object, move construct would be called. P.P.S. I added move constructor to B
B(B&& rhs) { std::cout << "B move constructor" << std::endl; }
and is never called either.
The standard says that a move constructor is generated by default only for classes that don' t have a copy constructor. Otherwise the copy constructor is invoked. It has to be like that for compatibility reasons: move constructors were introduced only in C++11. As you did, I've also tried adding the move constructor with the same results :)
Mircea
-
I stepped through that code with the debugger, and it appears that it treats
new B (B (aobj));
the same asnew B (aobj);
. That is to say, it optimises out the creation of the interim object, as it is never needed.I was also ready to blame the compiler except that I was using the code in debug mode with all optimizations turned off. You can try it even with something like
i=1;i=2;
and you will see that it is generating not optimized code. Besides, my code is abstracted from a real world program where the copy constructor was making a deep copy of an object full of pointers and memory allocations. I cannot see the compiler simply throwing away all those side-effects. Also I tried with both VisualStudio and g++ and both compilers behave the same way. It must be something deeper in this than a compiler bug. As I use to say in these cases: the silly is on the other side of the screen :)Mircea
-
I feel I'm turning slightly mad. In my mind the two variants below should give the same output:
class A {
public:
A () { cout << "A created" << endl; }
};class B {
public:
B () { cout << "Default B constructor" << endl; }
B (A oa) { cout << "B created from A" << endl; }
B (const B& other) { cout << "B copy constructor" << endl; }
};
int main()
{
A aobj;
B* bptr = new B (B (aobj));cout << "--------" << endl;
B bobj (aobj);
B* bbptr = new B (bobj);
}After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:
A created
B created from AB created from A
B copy constructorWhat am I missing here?
Mircea
A more thorough use of Google brought me the this:
Quote:
Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).
This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case
B* bptr = new B(B(aobj));
can be transformed intoB* bptr = new B(aboj);
even if the copy constructor has side-effects.Mircea
-
A more thorough use of Google brought me the this:
Quote:
Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).
This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case
B* bptr = new B(B(aobj));
can be transformed intoB* bptr = new B(aboj);
even if the copy constructor has side-effects.Mircea
-
A more thorough use of Google brought me the this:
Quote:
Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).
This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case
B* bptr = new B(B(aobj));
can be transformed intoB* bptr = new B(aboj);
even if the copy constructor has side-effects.Mircea
I'm somewhat impressed by this compiler, but the compiler is allowed to means that it could behave differently under different compilers, especially if the construction of the temporary object has side effects.
Robust Services Core | Software Techniques for Lemmings | Articles
The fox knows many things, but the hedgehog knows one big thing. -
I'm somewhat impressed by this compiler, but the compiler is allowed to means that it could behave differently under different compilers, especially if the construction of the temporary object has side effects.
Robust Services Core | Software Techniques for Lemmings | Articles
The fox knows many things, but the hedgehog knows one big thing.On the contrary: I'm quite unimpressed ("we are not amused"). You know what they say that C makes it easy to shoot yourself in the foot and C++ gives you a bazooka. In this case I's say you walk with hand grenades in your pocket; safety pins are optional. Note that even in the variant that 'works':
B bobj (aobj);
B* bbptr = new B (bobj);bobj
is susceptible to being optimized out of existence and turned back into the previous case. To leave it to the compiler, or the compiler designer, if a function should be called or not, and to do it without even a warning, is unconscionable. Haven't researched the history of this section of the standard but I'd dare say that it dates back to the '03 time and the 'efficiency wars'. That was put to rest with the '11 move constructors and assignment operators but probably that section of the standard slipped through. In fairness, I should say that it's not something that happens too often. This is the first time I run into the issue but I'll keep it in mind for the future.Mircea
-
On the contrary: I'm quite unimpressed ("we are not amused"). You know what they say that C makes it easy to shoot yourself in the foot and C++ gives you a bazooka. In this case I's say you walk with hand grenades in your pocket; safety pins are optional. Note that even in the variant that 'works':
B bobj (aobj);
B* bbptr = new B (bobj);bobj
is susceptible to being optimized out of existence and turned back into the previous case. To leave it to the compiler, or the compiler designer, if a function should be called or not, and to do it without even a warning, is unconscionable. Haven't researched the history of this section of the standard but I'd dare say that it dates back to the '03 time and the 'efficiency wars'. That was put to rest with the '11 move constructors and assignment operators but probably that section of the standard slipped through. In fairness, I should say that it's not something that happens too often. This is the first time I run into the issue but I'll keep it in mind for the future.Mircea
Yes. I'm very unimpressed by the spec but still impressed that the compiler bothered to implement this "optimization".
Robust Services Core | Software Techniques for Lemmings | Articles
The fox knows many things, but the hedgehog knows one big thing.