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++.
Related
I am playing around with std::function and std::bind to understand how arguments are copied around and if I can save some of the copy operations.
I understand that when using std::bind, the arguments are passed by value and not reference (unless std::ref is specified). However, when I run the following snippet, the copy constructor is invoked twice. Can someone explain why?
struct token
{
static int i;
int code;
token()
: code(i++)
{
cout << __FUNCTION__ << ": " << code << endl;
}
virtual ~token()
{
cout << __FUNCTION__ << endl;
}
token (token const & other)
: code (other.code)
{
cout << "copy ctor: " << code << endl;
}
// update -- adding a move ctor
token (token const && other)
: code (std::move(other.code))
{
cout << "move ctor: " << code << endl;
}
// update -- end
void boo() const
{
cout << __FUNCTION__ << ": " << code << endl;
}
};
void call_boo(token const & t)
{
t.boo();
}
int main()
{
token t2;
cout << "default" << endl;
std::function< void () >(std::bind(&call_boo, t2));
cout << "ref" << endl;
std::function< void () >(std::bind(&call_boo, std::ref(t2)));
cout << "move" << endl;
std::function< void () >(std::bind(&call_boo, std::move(t2)));
cout << "end" << endl;
return 0;
}
When run, this produces the following output:
token: 1
default
// Without move ctor
// copy ctor: 1 // Makes sense. This is the passing by value.
// copy ctor: 1 // Why does this happen?
// With move ctor
copy ctor: 1
move ctor: 1
~token
~token
ref // No copies. Once again, makes sense.
move
// Without move ctor
// copy ctor: 1
// copy ctor: 1
// With move ctor
move ctor: 1
move ctor: 1
~token
~token
end
~token
Parameters for this constructor of std::function are always copied, so a copy of the bind object (which itself has a copy of your token object) is created inside the std::function.
http://en.cppreference.com/w/cpp/utility/functional/function/function
template< class F >
function( F f );
5) Initializes the target with a copy of f. If f is a null pointer to function or null pointer to member, *this will be empty after the call. This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R. (since C++14)
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.
};
I have a situation which I cannot wrap my head around. I define a non-copy-able struct and want to in-place construct it in a tuple. If I do so, it is copied. I thought the problem may be std::make_tuple, but it isn't.
If instead I construct the object, then move it in the tuple, things work as expected.
Demonstration (I use std::exit to prevent the output of normal destruction).
#include <tuple>
#include <iostream>
#include <cstdlib>
struct MoveMe {
MoveMe(size_t s) : size(s) {}
MoveMe(MoveMe&&) = default;
MoveMe(const MoveMe&) = delete;
MoveMe& operator=(MoveMe&&) = default;
MoveMe& operator=(const MoveMe&) = delete;
~MoveMe() {
std::cout << "Destroyed" << std::endl;
}
size_t size = 0;
};
int main(int, char**) {
std::cout << "Constructed in-place." << std::endl;
auto tuple = std::make_tuple(MoveMe{10});
std::cout << std::endl << "Other tests." << std::endl;
std::tuple<MoveMe> tuple2 = std::tuple<MoveMe>(MoveMe{10});
std::cout << std::endl << "Moved." << std::endl;
MoveMe obj{10};
auto tuple3 = std::make_tuple(std::move(obj));
std::exit(0);
// return 0;
}
Outputs
Constructed in-place.
Destroyed
Other tests.
Destroyed
Moved.
Any ideas why that is? My understanding of rvalues is basic, so my guess is I'm missing something obvious. Thank you.
It isn't copied it is still moved.
In your case, you're creating a temporary (MoveMe{10}) that is then used to move construct the instance of MoveMe in the tuple. After the instance of MoveMe in the tuple is move constructed, the temporary that was moved is destroyed.
You can directly construct the MoveMe object in the tuple by forwarding the arguments
std::cout << "Directly Constructed" << std::endl;
auto tuple = std::tuple<MoveMe>(10);
Which will not result in a temporary being created then destroyed.
So I have a perfect forwarder, and I want to appropriately capture it in a lambda, such that R-values are copied in, and L-values are captured by reference. However simply using std::forward doesn't do the job, as evidenced by this code:
#include<iostream>
class testClass
{
public:
testClass() = default;
testClass( const testClass & other ) { std::cout << "COPY C" << std::endl; }
testClass & operator=(const testClass & other ) { std::cout << "COPY A" << std::endl; }
};
template< class T>
void testFunc(T && t)
{ [test = std::forward<T>(t)](){}(); }
int main()
{
testClass x;
std::cout << "PLEASE NO COPY" << std::endl;
testFunc(x);
std::cout << "DONE" << std::endl;
std::cout << "COPY HERE" << std::endl;
testFunc(testClass());
std::cout << "DONE" << std::endl;
}
Compiling this with
g++ -std=c++14 main.cpp
Produces the output
PLEASE NO COPY
COPY C
DONE
COPY HERE
COPY C
DONE
In a perfect world, I would like to only have the "COPY C" appear in the rvalue case, not the lvalue case.
My work around would be to use a helper function overloaded for L- and R- values, but I was wondering if there was a better way.
Cheers!
You may use the following:
[test = std::conditional_t<
std::is_lvalue_reference<T>::value,
std::reference_wrapper<std::remove_reference_t<T>>,
T>{std::forward<T>(t)}]
Live Demo
but providing helper function seems more readable
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)