In c++98, the following program is expected to call the copy constructor.
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "default" ; }
A(int i) { cout << "int" ; }
A(const A& a) { cout << "copy"; }
};
int main ()
{
A a1;
A a2(0);
A a3 = 0;
return 0;
}
That is evident if you declare the copy constructor explicit in above case (the compiler errors out). But I don't I see the output of copy constructor when it is not declared as explicit. I guess that is because of copy elision. Is there any way to disable copy elision or does the standard mandates it?
Pre C++ 17
A a3 = 0;
will call copy constructor unless copy is elided. Pass -fno-elide-constructors flag
from C++17, copy elision is guaranteed. So you will not see copy constructor getting called.
You have wrong understanding what the copy elision is. Please refer to this question for more info.
In this particular case, if you define the constructor explicit, it will cause an error because A a3 = 0; on this line the compiler created a object using 0.
Related
Here, i am getting different out on different compiler, why is that ?
On msvc compiler, there i'm getting extra destructor statement ?
Why i'm getting this behaviour ?
Am i missing something ?
i had looked many question on stackoverflow, but i can't find anything related to my problem ?
i also tried to look for duplicate, but didn't find one.
class A {
public:
A()
{
std::cout << "A::constructor" << "\n";
}
~A()
{
std::cout << "A::Destructor" << "\n";
}
int x = 0;
int y = 0;
};
class B {
public:
A member_var_1;
int member_var_2;
B()
{
std::cout << "B::constructor" << '\n';
}
B(A a, int b)
{
member_var_1 = a;
member_var_2 = b;
std::cout << "B(A, int)::constructor " << '\n';
}
~B()
{
std::cout << "B::destructor" << '\n';
}
};
int main()
{
B v1 {A(), 5};
}
GCC output:
A::consturctor // parameterized constructor first argument constructor
A::consturctor // construction of B's class member (member_var_1)
B(A, int)::consturcotr // B class parameterized constructor
A::Destructor // Destruction of argument of parameterized constructor
B::destructor // object goes out of scope, so B destructor called
A::Destructor // B's Destructor called member's destructor
MSVC output:
A::consturctor
A::consturctor
B(A, int)::consturcotr
A::Destructor
A::Destructor // what is it destroying? if i define a "class A" copy constructor, then i don't get this output.
B::destructor
A::Destructor
Since you're using C++17 and there is mandatory copy elision from C++17(&onwards), the extra destructor call must not be there.
A msvc bug has been reported as:
MSVC produces extra destructor call even with mandatory copy elision in C++17
Note that if you were to use C++11 or C++14, then it was possible to get an extra destructor call because prior to c++17 there was no mandatory copy elision and the parameter a could've been created using the copy/move constructor which means that you'll get the fourth destructor call as expected. You can confirm this by using the -fno-elide-constructors flag with other compilers. See Demo that has a contrived example of this.
B v1 {A(), 5};
In MSCV compiler, first, the construction of temporary A() is happening then it is elided to to "a" argument of parameterized constructor. that's why the extra destruction is happening for this temporary but it shouldn't be here because it is done implicitly.
So it is a bug in MSCV.
I have some code that returns a class object by value and the copy constructor is being called more than I thought it would. This seems to be how the g++ compiler does things but I'm not sure why. Say I have this code:
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "constructor" << endl; }
A(const A& other) { cout << "copy constructor" << endl; }
~A() { cout << "destructor" << endl; }
};
A f()
{
return A();
}
int main()
{
cout << "before f()\n";
A a = f();
cout << "after f()\n";
return 0;
}
When compiled with constructor elision turned off
g++ test.cpp -fno-elide-constructors
it outputs this:
before f()
constructor
copy constructor
destructor
copy constructor
destructor
after f()
destructor
So the copy constructor is called twice. I can see why the copy constructor would be called when copying the return value of f() into the variable a but why again?
I've read about C++ compilers returning temporary objects from functions but I don't understand the point of that. Even with elision turned off it seems unnecessary to create a temporary object for every function return value.
Update
Just to be super clear here, I'm not surprised that the copy constructor is being called. I expect that since I turned off elision. What I'm confused about is why the copy constructor needs to be called twice. It seems like once should be enough to copy the result from f() into a.
Pre-C++17
In Pre-C++17 standard there was non-mandatory copy elison, so by using the -fno-elide-constructors flag you are disabling the return value optimization and some other optimizations where copies are elided.
This is what is happening in your program:
First due to the return statement return A(); an object is constructed using the default constructor A::A().
Next, a copy of that default constructed object is returned to the caller. This is done using the copy constructor A::A(const A&). Hence you get the first copy constructor call output.
Finally, due to the statement A a = f(); which is copy initialization, the object named a is created as a copy of that returned value using the copy constructor A::A(const A&). Hence you get the second copy constructor call output.
//--v----------------------->1st copy constructor called due to this(return by value)
A f()
{
//-------------vvv---------->default constructor called due to this
return A();
}
//--vvvvvvvvv--------------->2nd copy constructor called due to this(copy initialization)
A a = f();
C++17
In C++17(and onwards), due to mandatory copy elison, you will not get any copy constructor call output. Demo.
It's because pre C++17, compilers were not mandated to elide the copies. If you do turn on compiling this as C++17 or later, there will only be one constructor call - even if you use -fno-elide-constructors.
Demo
The instances in C++14 with -fno-elide-constructors are created like this:
A // 2:nd (1:st copy)
f()
{
return A();
// 1:st
}
A a = f();
// 3:rd (2:nd copy)
If you instead return {};, you can skip the first temporary instance and only get one copy:
A // 1:st
f()
{
return {};
// arg to ctor
}
A a = f();
// 2:nd (1:st copy)
Demo
I have some code that returns a class object by value and the copy constructor is being called more than I thought it would. This seems to be how the g++ compiler does things but I'm not sure why. Say I have this code:
#include <memory>
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "constructor" << endl; }
A(const A& other) { cout << "copy constructor" << endl; }
~A() { cout << "destructor" << endl; }
};
A f()
{
return A();
}
int main()
{
cout << "before f()\n";
A a = f();
cout << "after f()\n";
return 0;
}
When compiled with constructor elision turned off
g++ test.cpp -fno-elide-constructors
it outputs this:
before f()
constructor
copy constructor
destructor
copy constructor
destructor
after f()
destructor
So the copy constructor is called twice. I can see why the copy constructor would be called when copying the return value of f() into the variable a but why again?
I've read about C++ compilers returning temporary objects from functions but I don't understand the point of that. Even with elision turned off it seems unnecessary to create a temporary object for every function return value.
Update
Just to be super clear here, I'm not surprised that the copy constructor is being called. I expect that since I turned off elision. What I'm confused about is why the copy constructor needs to be called twice. It seems like once should be enough to copy the result from f() into a.
Pre-C++17
In Pre-C++17 standard there was non-mandatory copy elison, so by using the -fno-elide-constructors flag you are disabling the return value optimization and some other optimizations where copies are elided.
This is what is happening in your program:
First due to the return statement return A(); an object is constructed using the default constructor A::A().
Next, a copy of that default constructed object is returned to the caller. This is done using the copy constructor A::A(const A&). Hence you get the first copy constructor call output.
Finally, due to the statement A a = f(); which is copy initialization, the object named a is created as a copy of that returned value using the copy constructor A::A(const A&). Hence you get the second copy constructor call output.
//--v----------------------->1st copy constructor called due to this(return by value)
A f()
{
//-------------vvv---------->default constructor called due to this
return A();
}
//--vvvvvvvvv--------------->2nd copy constructor called due to this(copy initialization)
A a = f();
C++17
In C++17(and onwards), due to mandatory copy elison, you will not get any copy constructor call output. Demo.
It's because pre C++17, compilers were not mandated to elide the copies. If you do turn on compiling this as C++17 or later, there will only be one constructor call - even if you use -fno-elide-constructors.
Demo
The instances in C++14 with -fno-elide-constructors are created like this:
A // 2:nd (1:st copy)
f()
{
return A();
// 1:st
}
A a = f();
// 3:rd (2:nd copy)
If you instead return {};, you can skip the first temporary instance and only get one copy:
A // 1:st
f()
{
return {};
// arg to ctor
}
A a = f();
// 2:nd (1:st copy)
Demo
I have a following code:
#include <iostream>
using namespace std;
struct A
{
A() {}
A(const A&) { cout << "copy const" << endl; }
A(A&) { cout << "copy non const" << endl; }
};
A f(A a)
{
return a;
}
int main() {
A a1 = f(A());
}
The A(A&) copy constructor is called. Why A(const A&) is not called since I pass a temporary object?
When I comment out the A(const A&) copy constructor the program does not compile.
What you are seeing is a mix of copy elision and an actual copy being made. Since f takes a by value, It needs to copy A() into a. The compiler sees this copy really isn't needed, so it elides that and instead directly constructs a so you don't see any call. In the body of f when you return a;, it needs to copy a into the return value. Since a is a lvalue, A(A&) is a better match than A(const A&) so you see the call to the non const copy constructor. Then a1 needs to be initialized from f's return value. Again copy elision comes into play again and instead of seeing a copy it just directly puts the return value into a1's storage.
So, elide, non-const copy, elide, which leaves the output with just copy non const
You get an error when you remove A(const A&) because even though those copies were elided, C++ still required there to be copy constructor until C++17.
If you compile with gcc or clang and use -fno-elide-constructors you can actually see those copies. You can see that in this live example. Note that I used -std=c++11 to turn off C++17's guaranteed copy elision
#include<iostream>
using namespace std;
class A{
public:
static int cnt;
A()
{
++cnt;
cout<<"constructor:"<<cnt<<endl;
}
~A()
{
--cnt;
cout<<"destructor:"<<cnt<<endl;
}
};
int A::cnt = 0;
A f(A x){
return x;
}
int main(){
A a0;
A a1 = f(a0);
return 0;
}
The program will output:
constructor:1
destructor:0
destructor:-1
destructor:-2
The constructor and destructor don't appear in pairs?
You need to add a copy constructor that increases the counter.
A(const A&)
{
++cnt;
cout<<"copy constructor:"<<cnt<<endl;
}
If you don't add it explicitly, the compiler generates one that does nothing with the counter cnt.
This expression
A a1 = f(a0);
is creating copies of a0, which make use of the copy constructor. The exact number of copies may vary depending on copy elision, but your cnt should be 0 at the end of the program.
Note: In C++11, you should also consider the possibility of a compiler generated move copy constructor, however, once you declare your own copy constructor, the compiler no longer generates the move version.
You are not tracking all constructors, only the default constructor. The compiler has generated a copy constructor and used it a couple of times, accounting for the 2 objects that are listed as destroyed and not as created.
You need to count copy constructor calls as well. In C++11 there are also move constructors that need to be taken into account.