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.
Related
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
So i have this class called point, just to log to the console everytime an object gets constructed and destroyed. And i did the following:
#include <iostream>
struct point{
point() {
std::cout << "ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};
int main(){
int x = 3;
point* ptr = new point[x]{point()};
delete[] ptr;
}
ctor
dtor
dtor
dtor
This ended up calling the constructor just once, and the destructor 3 times, why? I know that this is bad, probably ub, but i would like to understand why. This other allocations give me the "expected" output:
int x = 3;
constexpr int y = 3;
point* ptr1 = new point[3];
point* ptr2 = new point[x];
point* ptr3 = new point[y]{point()};
ctor
ctor
ctor
dtor
dtor
dtor
Im using visual studio 19 latest version.
This is a compiler bug.
By using operator new without a constant defined type size MSVC compiler will call the class object constructor and destructor as many times as explicitly specified at initializer list and/or array size.
#include <iostream>
struct point {
point() {
std::cout << "ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};
int main() {
int x = 3;
point* ptr = new point[x]{point()};
delete[] ptr;
}
As stated will call as explicitly specified point ctor once.
This can be asserted by: point* ptr = new point[x]{point(), point()};
MSVC Output: ctor ctor dtor dtor dtor.
GCC: ctor ctor ctor dtor dtor dtor (which should be guaranteed)
And even a throwable array out of bound exception UB: point* ptr = new point[x]{point(), point(), point(), point(), point() }; follows the behavior.
MSVC Output: ctor ctor ctor ctor ctor dtor dtor dtor.
GCC: terminate called after throwing an instance of 'std::bad_array_new_length'
Too many initializers is correctly detected if the defined size is constant. i.e const int x = 3 or constexpr int x = 3
This might not give you any direct answer, but just presenting some observations.
As #interjay has commented, we could see that there is a difference between how the same piece of code runs in VC++ and other popular compilers. I didn't see any deviation from standards were mentioned in MSDN, and infact VC++ do support for list and other iniliazilers following the standards in VS2017.
Now while playing with different notations of rewriting the same thing, what I observed was something interesting.
Verifying what is documented in MSDN I see that for the following notation,
point* ptr = new point[x]{};
Output is as expected, and functionally it is consistent across different compilers.
ctor
ctor
ctor
dtor
dtor
dtor
Also with the following stack based array allocation,
point ptr[3] = {point()};
gets us
ctor
ctor
ctor
dtor
dtor
dtor
So far everything looks good and the results matches with other compilers, not until we call the following code:
point* ptr = new point[x]{point()};
Just coming with best of what I understand from this is that this could be a implementation glitch in VC++.
As you call new operator along with constructor initialization inside the braces, may be for VC++ this is not a recommended usage, and internally it falls back to some custom initialization handler where it is expected to call constructors when explicitly assigned for each indices, while rest everything else is just allocated with uninitialized memory (no constructor gets called). This is a clear undefined behavior (UB).
While, in the couple of examples that I have shown earlier, it seems like (looking at the call stack in Windbg, I could be entirely wrong with this) internally VC++ at the runtime calls something similar to vector constructor initializer which calls the default constructor (only) iteratively for all indices.
Again this can be verifies using following piece of code, where calling a constructor overload
point(int i) {
std::cout << "ctor "<<i<<"\n";
}
from
point ptr[3] = {point(10)}; //not a default constructor
spits out:
ctor 10
ctor
ctor
dtor
dtor
dtor
This is weird and an abnormal behavior, which is nothing new in VC++. What I can suggest is that try using the following notation to get consistant result in cross different compilers:
point* ptr = new point[x]{};
This ended up calling the constructor just once, and the destructor 3 times, why?
You need to log the copy and move constructors as well:
#include <iostream>
struct point{
point() {
std::cout << "default ctor\n";
}
point(const point &) {
std::cout << "copy ctor\n";
}
point(point &&) {
std::cout << "move ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};
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.
I'm currently trying to get a hang of move constructor.
I came upon the following (Compiled using g++ d.cpp --std=c++11 -O3)
class A {
string _x;
public:
A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x; }
A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x; }
A foo() {
cout << "foo: " << _x << "\n";
return A("foo");
}
};
int main()
{
A a;
A b = a;
b.foo();
}
I expect this to output:
default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo
However the output is:
default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo
Why isn't the A b = a line optimized to use the move constructor? The a object is never used afterwards, so it would be safe to optimize the code to use it instead of the copy constructor.
I know I could force the move contructor to be invoked with std::move(), but I'd prefer this to happen automatically in cases like this one.
Why isn't the A b = a line optimized to use the move constructor?
What you can do in copy constructor and move constructor could be totally different. The compiler cannot guarantee that the results of the two constructors are identical. Implementing this kind of optimization has the potential of changing the behavior of your program, which breaks the as-if rule.
You need to use std::move to cast a to A&&:
#include <utility>
int main()
{
A a("a");
A b = std::move(a);
b.foo();
}
A correct implementation of the move constructor should be:
A(A&& other)
: _x(std::move(other._x))
{}
After the line A b = std::move(a);, a should be "empty". In this case, a._x will be empty. as pointed by #TonyD in the comments, a._str could be in an unspecified but valid state (move constructor of std:string). You should use a with caution after this line.
A b = a; always invokes the copy constructor, no matter if it could invoke the move constructor. Additionally the lifetime of the object a continues after the assignment, even it is not used anymore.
If you want to use the move constructor, you have to make it explicit:
A b = std::move(a);
Note that this can be dangerous, as a is still accessible after the move. If you accidentally use it later, there may be undefined behavior.
Think about why it should happen automatically. In the example you gave, there is no need, as you can as well use a instead of b. In many cases where it would make more sense move constructor/assignment would be used automatically, e.g. A a; a = foo();.
Why isn't the A b = a line optimized to use the move constructor?
Because that would change the observable behavior of the program. The compiler is not permitted to freely change the observable behavior of the program (§1.9/1), except under very specific circumstances (§12.8/31). This is not one of those circumstances. Remove the side effects from your constructors, and the compiler may optimize them away. Of course, if you remove the side effects, then you won't notice if the compiler optimizes the constructor calls away (unless you examine the assembly or binary output), but that's the whole point.