I simplified my code down to the root of the trouble:
//==============================================================================
// PRE-DEFINITIONS
#define GIVE_ME_ODD_BEHAVIOUR true
//==============================================================================
// INCLUDES
#include <iostream>
//==============================================================================
// TYPES
//------------------------------------------------------------------------------
template<typename T> struct X {
T data;
X() : data(0)
{ std::cout << "X construction # " << this << std::endl; }
template<typename TT>
X(const X<TT> & other) : data(other.data)
{ std::cout << "X construction # " << this << " from " << &other << std::endl; }
~X()
{ std::cout << "X destruction # " << this << std::endl; }
template<typename TT>
void Copy(const X<TT> & other)
{ std::cout << "X copy # " << this << " from " << &other << std::endl; }
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<typename T>
X<double> XConversion(const X<T> & other)
{
#if GIVE_ME_ODD_BEHAVIOUR
return X<double>(other);
#else
X<double> d;
d.Copy(other);
return d;
#endif
}
//==============================================================================
// MAIN
int main()
{
X<double> d;
X<int> i;
std::cout << std::endl;
d = XConversion(i);
std::cout << std::endl;
d = XConversion(d); // !!!
std::cout << std::endl;
return 0;
}
which, with
GIVE_ME_ODD_BEHAVIOUR true
gives the output:
X construction # 0x23aa70
X construction # 0x23aa60
X construction # 0x23aa80 from 0x23aa60
X destruction # 0x23aa80
X destruction # 0x23aa90 // never created !!!
X destruction # 0x23aa60
X destruction # 0x23aa70
It seems similar to THIS problem but i do have the ctor defined. I see the copy elision optimisation point here but:
why the copiler destructs the object that it optimised out and thus never created?
how can I ensure that it does not happen ?
Additional info:
I tried gcc 4.8.1 and C++ Builder XE3, optimisation off, debug compilation, both with the same result.
I tried to remove the template-ness like
struct X {
double data
...
X(const X &)
...
};
but with the same result.
One way to solve this would be, e.g., to make the cctor private and to public the Copy method only. But this would prevent me (anybody) from even returning the object from a function...
Background:
I have a set of template classes that are mutually friends and convertible. The classes share the same data but manipulate them in different ways via partial specialisation. The instances may either do soft or raw copy of the data as needed. If they do the soft copy, ref counter is increased. With the extra deletion of the instance that was actually never created, the ref counter is decreased without its prior incrementation.
Seems the compiler generates a copy constructor for your case, maybe it does not like the one with the templates (if I recall correctly copy constructors are not (or may not be) template based ... might be wrong here though)... see: Copy constructor of template class
Adding the following code:
X(const X & other) : data(other.data)
{ std::cout << "X Copy construction # " << this << " from " << &other << " as " << typeid(X).name() << std::endl; }
template<typename TT>
X(const X<TT> & other) : data(other.data)
{ std::cout << "X construction # " << this << " from " << &other << " as " << typeid(TT).name() << std::endl; }
gives the following output:
X construction # 0x7fff3496c040
X construction # 0x7fff3496c038
X construction # 0x7fff3496c020 from 0x7fff3496c038 as i
X destruction # 0x7fff3496c020
X Copy construction # 0x7fff3496c018 from 0x7fff3496c040 as 1XIdE
X destruction # 0x7fff3496c018
X destruction # 0x7fff3496c038
X destruction # 0x7fff3496c040
so there is your missing object. (tested both with clang and g++, same behaviour)
Related
I came across the following problem, with two classes X and Y where Y has a member of type X
If I then:
create a vector of objects of type Y via std::vector<Y> list = {{}}
and Y has a defaulted default constructor
One of the instances of type X will be destroyed twice. Even when using rule of three (or five).
It does not happen, if I either create the vector via {Y{}} or define a user supplied default constructor for Y.
See the console output of the following example:
#include <iostream>
#include <vector>
class X
{
public:
X()
{
std::cout << "constructed " << this << std::endl;
}
~X()
{
std::cout << "deleted " << this << std::endl;
}
X(const X& other)
{
std::cout << "copy constructed " << &other << " to " << this << std::endl;
}
};
class Y
{
public:
//Y(){}; // works
Y() = default; // doesn't work
~Y() = default;
Y(const Y& y) = default; // copy constructor
Y& operator=(const Y& y) = default; // copy assignment
// Y(Y&& other) noexcept = default; // move constructor
// Y& operator=(Y&& other) noexcept = default; // move assignment
private:
X x;
};
int main()
{
std::cout << "start" << std::endl;
std::vector<Y> list = {{}}; // doesn't work
//std::vector<Y> list = {Y{}}; // works
std::cout << "done" << std::endl;
return 0;
}
Outputs
start
constructed 000000271CAFFCD0
copy constructed 000000271CAFFCD0 to 000001727F7145B0
deleted 000000271CAFFCD0
deleted 000000271CAFFCD0
done
deleted 000001727F7145B0
https://godbolt.org/z/vscP4TexE
This is only reproducable in MSVC.
Am I missing something or is this a compiler bug?
Edit
Thanks for the hint about the missing return statements.
I added them, but get the same results.
I did enable warnings, but it only shows that the unreferenced inline functions are removed.
Edit 2
Since they are unused, I removed copy and move constructors and assignment operators from the example.
Edit 3
Adapted Output to the new code example. It still contains the double delete. Note that MSVC is a bit flakey on godbolt and it sometimes does not compile the example.
I have a strange behavior with object assignments. I will much appreciate, if you can explain why this assignment works like this. It has cost me already a lot of time.
I am using Visual Studio Enterprise 2017 (all default settings).
Code:
#include "stdafx.h"
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << "Constructor of " << this << endl;
}
~Test()
{
cout << "Destructor of " << this << endl;
}
};
int main()
{
cout << "Assignment 1" << endl;
auto t = Test();
cout << "Assignment 2" << endl;
t = Test();
int i = 0;
cin >> i;
return 0;
}
Output (up to cin):
Assignment 1
Constructor of 006FFC9F
Assignment 2
Constructor of 006FFBC7
Destructor of 006FFBC7
Expected Output (up to cin):
Assignment 1
Constructor of 006FFC9F
Assignment 2
Destructor of 006FFC9F
Constructor of 006FFBC7
I wanted to write a test function which creates an object of my (template) class, do some tests, then create a new object and do some more testing. The problem is that t holds the already destructed object after the second assignment.
I know that I can just use dynamic allocation which results in the expected behavior, but why does this program behave different?
Thank you very much.
Regards.
PS: Results are the same, independent of Release/Debug or 64/32 bit compilation
EDIT: More verbose example:
#include "stdafx.h"
#include <iostream>
using namespace std;
class Test
{
private:
float* val;
public:
Test()
{
val = new float;
cout << "Constructor of " << this << ", addr. of val: " << val << endl;
}
~Test()
{
cout << "Destructor of " << this << ", addr. of val: " << val << " --> DELETING VAL!" << endl;
delete val;
}
float* getVal() { return this->val; }
};
int main()
{
cout << "Assignment 1" << endl;
auto t = Test();
cout << "Assignment 2" << endl;
t = Test();
cout << "Val Address: " << t.getVal() << endl;
int i = 0;
cin >> i;
return 0;
}
Output (it holds a deleted pointer at the end!!!):
Assignment 1
Constructor of 004FFBDC, addr. of val: 0072AEB0
Assignment 2
Constructor of 004FFB04, addr. of val: 00723928
Destructor of 004FFB04, addr. of val: 00723928 --> DELETING VAL!
Val Address: 00723928
With
auto t = Test();
you actually construct two objects. First is the Test() which constructs a temporary object. The second is the construction of t which is made through copy-construction. There is no assignment being made here, even if the = operator is used, it's copy-construction.
If you add a copy-constructor to the Test class similar to your constructor and destructor, you should see it clearly.
As for
t = Test();
here a temporary object is created with Test(). That temporary object is then passed to the (compiler-generated) assignment operator of the Test class, and then the temporary object is promptly destructed.
The object t itself is not destructed, it should not be as it is the destination of the assignment.
Your confusion seems to be a mistaken expectation that the original object is destroyed when assignment takes place. Like, in this code:
cout << "Assignment 2" << endl;
t = Test();
This piece of code invokes the move-assign operator. Since you didn't define one, the default one generated by the compiler is more-or-less going to look like this:
Test & operator=(Test &&) {}
Note how there's no invocation of a constructor or (critically) a destructor in that code. The only Constructors and Destructors that are going to run are on the temporary object (which is what you observe in the actual output). The original object doesn't get destroyed until the code goes out of scope; and why would it? It's not like you can stop using the stack space before then.
Edit: Something which might help you understand what's going on:
#include<iostream>
struct Test {
Test() {std::cout << "Constructed.\n";}
~Test() {std::cout << "Destructed.\n";}
Test(Test const&) {std::cout << "Copy-Constructed.\n";}
Test(Test &&) {std::cout << "Move-Constructed.\n";}
Test & operator=(Test const&) {std::cout << "Copy-Assigned.\n"; return *this;}
Test & operator=(Test &&) {std::cout << "Move-Assigned.\n"; return *this;}
};
int main() {
std::cout << "Test t;\n";
Test t; //Construction
std::cout << "Test t2(t);\n";
Test t2(t); //Copy-Construct
std::cout << "Test t3(std::move(t2));\n";
Test t3(std::move(t2)); //Move-Construct
std::cout << "Test t4 = t;\n";
Test t4 = t; //Copy Construct, due to Copy Ellision
std::cout << "Test t5 = Test();\n";
Test t5 = Test(); //Will probably be a normal Construct, due to Copy Ellision
std::cout << "t = t2;\n";
t = t2; //Copy Assign
std::cout << "t = Test();\n";
t = Test(); //Move Assign, will invoke Constructor and Destructor on temporary
std::cout << "Done! Cleanup will now happen!\n";
return 0;
}
Results as seen when compiled here:
Test t;
Constructed.
Test t2(t);
Copy-Constructed.
Test t3(std::move(t2));
Move-Constructed.
Test t4 = t;
Copy-Constructed.
Test t5 = Test();
Constructed.
t = t2;
Copy-Assigned.
t = Test();
Constructed.
Move-Assigned.
Destructed.
Done! Cleanup will now happen!
Destructed.
Destructed.
Destructed.
Destructed.
Destructed.
DOUBLE EDIT COMBO!:
As I mentioned in comments, val is just a pointer. 8 bytes (on a 64-bit machine) allocated as part of Test's storage. If you're trying to make sure that Test always contains a valid value for val that hasn't been deleted, you need to implement the Rule of Five (previously known as the Rule of Three):
class Test {
float * val;
public:
Test() {val = new float;}
~Test() {delete val;
Test(Test const& t) {
val = new float(*(t.val));
}
Test(Test && t) {std::swap(val, t.val);}
Test & operator=(Test const& t) {
float * temp = new float(*(t.val)); //Gives Strong Exception Guarantee
delete val;
val = temp;
return *this;
}
Test & operator=(Test && t) {std::swap(val, t.val); return *this;};
float & get_val() const {return *val;} //Return by reference, not by pointer, to
//prevent accidental deletion.
};
Simple program:
#include <iostream>
using namespace::std;
class X {
public:
X() {
cout << "Default Constructor called\n";
i = 0;
}
X(int i) {
cout << "Parameterized Constructor called\n";
this->i = i;
}
X(const X& x) {
cout << "Copy Constructor called\n";
i = x.getI();
}
~X() {
cout << "Destructor called\n";
}
int getI() const {
return i;
}
X func() {
cout << "Entered func\n";
X x(2);
return x;
}
private:
int i;
};
int main() {
X x1;
X x2 = x1.func();
cout << "Returned from func\n";
}
It outputs the following:
Default Constructor called
Entered func
Parameterized Constructor called
Copy Constructor called
Destructor called
Returned from func
Destructor called
Destructor called
After the 'Returned from func' is printed, no constructor is called when creating the instance x2. I was actually expecting a copy constructor to be called when instantiating x2 as it would have been if we did something like X x2 = x1;
Now, I was told that this is a result of RVO. Is it true?
In wiki, RVO is defined as:
Return value optimization, or simply RVO, is a compiler optimization technique that involves eliminating the temporary object created to hold a function's return value.
But, I don't buy this for two reasons:
1.x2 here is not a temporary object.
2. If it were really to be the case, then the compiler would have been much better off implementing RVO when x was being returned from the function. That was a genuine case of a temporary object (during the return statement).
So, please explain why x2 was not instantiated after the function returned an object of X.
It's much easier to see what's happening if you get your functions to output more precise information. Consider:
#include <iostream>
struct X
{
X() : i_(0) { std::cout << "X(" << this << ")\n"; }
X(int i) : i_(i) { std::cout << "X(" << this << ", i " << i << ")\n"; }
X(const X& rhs) : i_(rhs.i_) { std::cout << "X(" << this << ", const X& "
<< &rhs << ")\n"; }
~X() { std::cout << "~X(" << this << ")\n"; }
X func() { std::cout << "X::func(this " << this << ")\n"; X x(2); return x; }
int i_;
};
int main()
{
X x1;
X x2 = x1.func();
std::cout << "x1 " << &x1 << ", x2 " << &x2 << '\n';
}
Output on ideone.com was:
X(0xbfd346e8)
X::func(this 0xbfd346e8)
X(0xbfd346ec, i 2)
x1 0xbfd346e8, x2 0xbfd346ec
~X(0xbfd346ec)
~X(0xbfd346e8)
That shows full RVO and elided copy-construction, which will be typical of most compilers at normal production optimisation levels (and quite possibly even lower levels).
I suggest you run the above code on your compiler with whatever flags you've been using, and the addresses displayed should make it clearer to you exactly which objects are involved in the various operations you've observed. As is, your comments...
Observe carefully. That is called after the 'func entered' is printed. That means that it got called from within the function, i.e. it was referring to the instance x, not x2.
...is flawed logic (with more attitude to boot "I'll just wait for someone who actually understands copy constructors to answer this."), which the better logging should help you realise. (Then hopefully you'll apologise to Praetorian.)
I noticed something very strange with Visual Studio 2012: Defining a pair object like so:
auto objp = pair<int, LogMe>();
will not elide the copy/move of the pair in VC11, this call will print:
LogMe::LogMe - def.ctor!
LogMe::LogMe - move.ctor!
LogMe::~LogMe - dtor!
that is, a temporary pair will be created and then moved into the objp variable. (declaring it as pair<...> obj; only logs the default ctor)
I have cross checked with my LogMe test object alone:
cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();
# Construct Object via auto obj = ...
LogMe::LogMe - def.ctor!
and here the assignment will be elided.
This seems to be specific to VC11, as testing it in IDEOne (which uses gcc 4.8.1) shows that the extraneous move is elided always there.
What's going on here? Not being able to rely on the initialization copy being elided makes me nervous.
Note: Tests for release vs. debug version show the same result. (Which I would have expected, as copy-elision is performed independently of optimization flags in MSVC.)
Full sourcecode to test (See also the ideone link):
#include "stdafx.h"
#include <iostream>
#include <map>
using namespace std;
struct LogMe {
std::string member;
LogMe() {
cout << __FUNCTION__ << " - def.ctor!" << endl;
}
~LogMe() {
cout << __FUNCTION__ << " - dtor!" << endl;
}
LogMe(LogMe const&) {
cout << __FUNCTION__ << " - cpy.ctor!" << endl;
}
LogMe& operator=(LogMe const&) {
cout << __FUNCTION__ << " - cpy.assign.op!" << endl;
return *this;
}
LogMe(LogMe&&) {
cout << __FUNCTION__ << " - move.ctor!" << endl;
}
LogMe& operator=(LogMe&&) {
cout << __FUNCTION__ << " - move.assign.op!" << endl;
return *this;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
{
cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();
cout << "# Construct pair<int, object> via auto objp = ...\n";
auto objp = pair<int, LogMe>();
cout << "# Construct pair<int, object> via pair objp2; ...\n";
pair<int, LogMe> p2;
}
return 0;
It appears it is not the move ctor, nor the templated move ctor that is causing the problem, but the presence of enable_if<is_convertable<... in the templated move ctor:
Testing with just an object, throwing auto and pair out of the test:
OK, copy/move elided:
cout << "# Construct Object: auto obj = LogMe();\n";
LogMe obj = LogMe();
LogMe(LogMe&&) {
cout << __FUNCTION__ ...
}
And, with a test like so:
cout << "# Construct Object: LogMeTempl obj = LogMeTempl();\n";
LogMeTempl obj = LogMeTempl();
cout << "# Construct Object: LogMeTempl obj2;\n";
LogMeTempl obj2;
OK, copy move also elided:
template<class Other>
LogMeTempl(Other&& rhs
// , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
) {
cout << __FUNCTION__ << ...;
}
Fail! Move ctor invoked!
template<class Other>
LogMeTempl(Other&& rhs
, typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
) {
cout << __FUNCTION__ << ...;
}
And note that the enable_if can be reduced to enable_if<true, void>::type** = 0 - if fact any additional defaulted parameter will do (e.g. , int defaulted_param_on_move_ctor = 0 and it will still prevent the move elision).
This also extends to a type with a copy-ctor only that has a default argument. It won't be elided either. A quick cross-check with gcc shows there doesn't seem to be any such problem there.
Short Answer
Types with defaulted arguments in their copy/move ctor don't have their initialization copy/move elided.
I have added a bug on MS.connect for this issue.
I have also added a test-case for (N)RVO to IDEone. Even without the default argument, *N*RVO seems to work better in gcc than VC++.
If I have a constructor with n parameters such that any argument to that can be an rvalue and lvalue. Is it possible to do support this with move semantics for the rvalues without writing 2^n constructors for each possible rvalue/lvalue combination?
You take each one by value, like this:
struct foo
{
foo(std::string s, bar b, qux q) :
mS(std::move(s)),
mB(std::move(b)),
mQ(std::move(q))
{}
std::string mS;
bar mB;
qux mQ;
};
The initialization of the function parameters by the argument will either be a copy-constructor or move-constructor. From there, you just move the function parameter values into your member variables.
Remember: copy- and move-semantics are a service provided by the class, not by you. In C++0x, you no longer need to worry about how to get your own "copy" of the data; just ask for it and let the class do it:
foo f("temporary string is never copied", bar(), quz()); // no copies, only moves
foo ff(f.mS, f.mB, f.mQ); // copies needed, will copy
foo fff("another temp", f.mB, f.mQ); // move string, copy others
Note: your constructor only takes in values, those values will figure out how to construct themselves. From there, of course, it's up to you to move them where you want them.
This applies everywhere. Have a function that needs a copy? Make it in the parameter list:
void mutates_copy(std::string s)
{
s[0] = 'A'; // modify copy
}
mutates_copy("no copies, only moves!");
std::string myValue = "don't modify me";
mutates_copy(myValue); // makes copy as needed
mutates_copy(std::move(myValue)); // move it, i'm done with it
In C++03, you could emulate it fairly well, but it wasn't common (in my experience):
struct foo
{
foo(std::string s, bar b, qux q)
// have to pay for default construction
{
using std::swap; // swaps should be cheap in any sane program
swap(s, mS); // this is effectively what
swap(b, mB); // move-constructors do now,
swap(q, mQ); // so a reasonable emulation
}
std::string mS;
bar mB;
qux mQ;
};
Take the following code ideone link.
#include <iostream>
class A
{
public:
A() : i(0) {}
A(const A& a) : i(a.i) { std::cout << "Copy A" << std::endl; }
A(A&& a) : i(a.i) { std::cout << "Move A" << std::endl; }
int i;
};
template <class T>
class B1
{
public:
template <class T1, class T2>
B1(T1&& x1_, T2&& x2_) : x1(std::forward<T1>(x1_)), x2(std::forward<T2>(x2_)) {}
B1(const B1<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B1" << std::endl; }
B1(B1<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B1" << std::endl; }
private:
T x1;
T x2;
};
template <class T>
class B2
{
public:
B2(T x1_, T x2_) : x1(std::move(x1_)), x2(std::move(x2_)) {}
B2(const B2<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B2" << std::endl; }
B2(B2<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B2" << std::endl; }
private:
T x1;
T x2;
};
A&& inc_a(A&& a) { ++a.i; return static_cast<A&&>(a); }
A inc_a(const A& a) { A a1 = a; ++a1.i; return a1; }
int main()
{
A a1;
A a2;
std::cout << "1" << std::endl;
B1<A> b1(a1,a2);
std::cout << "2" << std::endl;
B1<A> b2(a1,A());
std::cout << "3" << std::endl;
B1<A> b3(A(),a2);
std::cout << "4" << std::endl;
B1<A> b4(A(),A());
std::cout << "5" << std::endl;
B2<A> b5(a1,a2);
std::cout << "6" << std::endl;
B2<A> b6(a1,A());
std::cout << "7" << std::endl;
B2<A> b7(A(),a2);
std::cout << "8" << std::endl;
B2<A> b8(A(),A());
std::cout << "9" << std::endl;
std::cout << std::endl;
std::cout << "11" << std::endl;
B1<A> b11(a1,a2);
std::cout << "12" << std::endl;
B1<A> b12(a1,inc_a(A()));
std::cout << "13" << std::endl;
B1<A> b13(inc_a(A()),a2);
std::cout << "14" << std::endl;
B1<A> b14(inc_a(A()),inc_a(A()));
std::cout << "15" << std::endl;
B2<A> b15(a1,a2);
std::cout << "16" << std::endl;
B2<A> b16(a1,inc_a(A()));
std::cout << "17" << std::endl;
B2<A> b17(inc_a(A()),a2);
std::cout << "18" << std::endl;
B2<A> b18(inc_a(A()),inc_a(A()));
std::cout << "19" << std::endl;
}
Which outputs the following:
1
Copy A
Copy A
2
Copy A
Move A
3
Move A
Copy A
4
5
Copy A
Copy A
Move A
Move A
6
Copy A
Move A
Move A
7
Copy A
Move A
Move A
8
9
11
Copy A
Copy A
12
Copy A
Move A
13
Move A
Copy A
14
Move A
Move A
15
Copy A
Copy A
Move A
Move A
16
Move A
Copy A
Move A
Move A
17
Copy A
Move A
Move A
Move A
18
Move A
Move A
Move A
Move A
19
As can be seen, the pass by value approach in B2 causes extra move for each argument in all cases except for when the argument is a prvalue.
If you want best performance, I suggest the template approach in B1. This way you have effectively have separate code for the copy and move cases, and hence only a single copy or a single move required. In the pass by value approach, at least two move/copies are required, except for the prvalue case where the compiler can construct the value in the place of the argument, in which as only one move is required.
Depending on what c++ compiler you are using, you could look into "functions with variable argument lists"
The idea is that you can pass in as many parameters as you want to the method and it just populates into an array that you can loop through.
For microsoft c++, the following blogposts might be helpful:
http://msdn.microsoft.com/en-us/library/fxhdxye9(v=VS.100).aspx
http://blogs.msdn.com/b/slippman/archive/2004/02/16/73932.aspx