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";
}
};
Related
Consider the following program:
#include <vector>
#include <iostream>
class A {
int x;
public:
A(int n) noexcept : x(n) { std::cout << "ctor with value\n"; }
A(const A& other) noexcept : x(other.x) { std::cout << "copy ctor\n"; }
A(A&& other) noexcept : x(other.x) { std::cout << "move ctor\n"; }
~A() { std::cout << "dtor\n"; } // (*)
};
int main()
{
std::vector<A> v;
v.emplace_back(123);
v.emplace_back(456);
}
If I run the program, I get (GodBolt):
ctor with value
ctor with value
move ctor
dtor
dtor
dtor
... which is in line with what I would expect. However, if on line (*) I mark the destructor as potentially throwing, I then get :
ctor with value
ctor with value
copy ctor
dtor
dtor
dtor
... i.e. the copy ctor is used instead of the move ctor. Why is this the case? It doesn't seem copying prevents destructions that moving would necessitate.
Related questions:
are std::vector required to use move instead of copy?
How to enforce move semantics when a vector grows?
Vector reallocation uses copy instead of move constructor
This is LWG2116. The choice between moving and copying the elements is often expressed as std::is_nothrow_move_constructible, i.e. noexcept(T(T&&)), which also erroneously checks the destructor.
tl;dr: Because std::vector prefers to offer you a "strong exception guarantee".
(Thanks goes to Jonathan Wakely, #davidbak, #Caleth for links & explanations)
Suppose std::vector were to use move construction in your case; and suppose that an exception were to be thrown during vector-resizing, by one of the A::~A calls. In that case, you would have an unusable std::vector, partially moved.
On the other hand, if std::vector performs copy construction, and an exception occurs in one of the destructors - it can simply ditch the new copy, and your vector will be in the same state it was before the resizing. That is the "strong exception guarantee" for the std::vector object.
The standard library designers chose to prefer this guarantee over optimizing the performance of vector resizing.
This had been reported as an issue/defect with the standard library (LWG 2116) - but after some discussion, it was decided to keep the current behavior as per the above consideration.
See also Arthur O'Dwyr's post: A "Pick any two" triangle for std::vector.
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 think it should, because it's important for correctness. However, I'm surprised to see Clang's output. Consider the code below:
#include <iostream>
struct S
{
int i;
S(int i) : i(i) {}
S(S&&)
{
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
S f()
{
S s{42};
std::cout << &s << "\n";
return s;
}
int main()
{
S s{f()};
std::cout << &s << "\n";
std::cout << s.i << "\n";
}
We defined a move ctor for S to check if S(S&&) is called, if not, NRVO is applied.
The result we see from GCC is:
0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42
NRVO is applied and they take the same address, which is expected.
However, Clang's output:
0x7fff908bbcc8
0x7fff908bbcf8
42
NRVO is applied but the addresses differ.
In case you wonder why having the same address is important - it's because some object may do some registration with its address at construction, and if the object is moved, it should be notified (e.g. via move-ctor).
Having NRVO applied but with different memory address thus makes it ill-formed.
It's a clear violation of the contract - no custom move/copy ctor is called, how could the compiler "copy" the data of S to a different place?
Is this a bug in Clang?
If we add a destructor to S, e.g.
~S() {}
This time, Clang outputs the same address.
Definitely seems to be a bug in clang, they should be the same, else things like the following will be erroneous
struct S
{
int i;
int* ptr;
S(int i) : i(i) {
this->ptr = &this->i;
}
S(S&& s)
{
this->i = s.i;
this->ptr = &this->i;
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
Where a move (or elision where addresses don't change) is required to ensure that the internal pointer points to the correct integer. But because of elision that pointer points to memory that does not contain the member integer.
See output here https://wandbox.org/permlink/NgNR0mupCfnnmlhK
As pointed out by #T.C., this is actually a bug in the Itanium ABI spec that doesn't take move-ctor into account. Quoting from Clang's dev:
Clang's rule is the one in the ABI: a class is passed indirectly if it
has a non-trivial destructor or a non-trivial copy constructor. This
rule definitely needs some adjustment [...]
Indeed, if we define either a non-trivial dtor or copy-ctor for S in the original example, we get the expected result (i.e. same address).
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.