I was trying to test a small code to check if my compiler (under Visual Studio 2019) does copy elision, as it is no more optional for some cases under C++17.
So I tried the code below :
#include <iostream>
#include <vector>
using namespace std;
struct myStruct
{
myStruct() { cout << "default ctor" << endl; }
myStruct(const myStruct& str) { cout << "copy ctor" << endl; }
myStruct& operator=(myStruct other) { cout << "Copy assignement" << endl; return *this; }
myStruct(myStruct&& str) noexcept { cout << "move ctor" << endl; }
myStruct& operator=(myStruct&& other) { cout << "move assignement" << endl; return *this; }
~myStruct() { cout << "deleted" << endl; }
};
myStruct get()
{
myStruct s;
return s;
}
int main()
{
vector<myStruct> vect;
vect.reserve(10);
cout << "Creating The Vector" << endl;
vect.push_back(get());
vect.push_back(get());
cout << "Cretion End" << endl;
return 0;
}
So as far as I read, calling the function get() will trigger the RVO so I will get only ctor calls. But when I run the program I get (I added <<<< in front of lines that are related to copy after calling get() function):
Creating The Vector
default ctor
move ctor
deleted
move ctor <<<<
deleted <<<<
default ctor
move ctor
deleted
move ctor <<<<
deleted <<<<
Cretion End
deleted
deleted
When tried with gcc I get :
Creating The Vector
default ctor
move ctor
deleted
default ctor
move ctor
deleted
Cretion End
deleted
deleted
So seems that Microsoft still didn't implement the Copy elision, or is there wrong in my code so I missed the real charm of Copy elision?
Thank you in advance
None of the copies in this case are required to be elided by any (currently existing) version of C++. The standard lays down several copies: the copy from s to the return value object of get, and the copy from the reference parameter to push_back into the vector's internal object. The latter copy cannot be elided, and the former copy's elision is not guaranteed to happen at all.
If you're talking about C++17's guaranteed elision, this only applies to the use of a prvalue to initialize an object (of that type). That never happens here (outside of the temporary parameter passed to call push_back, but that's normal for any version of C++), so it doesn't apply.
Here's a simple test: if the source object of the hypothetical copy has a variable name, then elision if applicable is not mandatory.
Related
struct Test {
int field = 30;
Test() { cout << "In ctor" << endl; }
Test(const Test &other) { field = other.field; cout << "In copy ctor" << endl; }
Test(Test &&other) { field = other.field; cout << "In move ctor" << endl; }
Test &operator=(const Test &other) { field = other.field; cout << "In copy assignment" << endl; return *this; }
Test &operator=(Test &&other) { field = other.field; cout << "In move assignment" << endl; return *this; }
~Test() { cout << "In dtor" << endl; }
};
Test get_test() {
Test t;
return t;
}
int main() {
Test t2 = get_test();
}
I think this is the canonical NRVO example. I'm compiling with -fno-elide-constructors and I see that the following are called: ctor, move ctor, dtor, dtor.
So the first ctor call corresponds to the line Test t;, the move ctor is constructing the t2 in main, then the temporary that is returned from get_test is destroyed, then the t2 in main is destroyed.
What I don't understand is: shouldn't there be a copy ctor invocation when returning by value? That is, I thought that get_test should be making a copy of t and then this copy is moved into t2. It seems like t is moved into t2 right away.
C++17
Starting from C++17, there is mandatory copy elison which says:
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
(emphasis mine)
This means that t2 is constructed directly from the prvalue that get_test returns. And since a prvalue is used to construct t2, the move constructor is used. Note that in C++17, the flag -fno-elide-constructors have no effect on return value optimization(RVO) and is different from NRVO.
Pre-C++17
But prior to C++17, there was non-mandatory copy elison and since you've provided the -fno-elide-constructors flag, a temporary prvalue is returned by get_test using the move constructor. So you see the first call to the move ctor. Then, that temporary is used to initialize t2 again using the move constructor and hence we get the second call to the move ctor.
I am trying to get the grasp of rvalue references and move semantics with a simple self-made example but I can't understand a specific part. I have created the following class:
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
I tried the following experiments to see if I can predict how the constructors/operators are going to be called:
A a1(1) - The default constructor is going to be called.
PREDICTED.
A a2 = a1 - The copy constructor is going to be called. PREDICTED.
a1 = a2 - The copy assignment operator is going to be called.
PREDICTED.
Now, I created a simple function that just returns an A object.
A helper() {
return A(1);
}
A a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. The move
constructor is not going to be called due to RVO. PREDICTED.
a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. Then, the move
assignment operator is going to be called. PREDICTED.
Now comes the part I don't understand. I created another function that is completely pointless. It takes an A object by value and it just returns it.
A helper_alt(A a) {
return a;
}
A a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then the move
constructor. PREDICTED.
a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then I thought that
the move assignment operator is going to be called BUT as I saw,
first, the move constructor is called and then the move assignment
operator is called. HAVE NO IDEA.
Please, if any of what I said is wrong or you feel I might have not understood something, feel free to correct me.
My actual question: In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?
Congratulations, you found a core issue of C++!
There are still a lot of discussions around the behavior you see with your example code.
There are suggestions like:
A&& helper_alt(A a) {
std::cout << ".." << std::endl;
return std::move(a);
}
This will do what you want, simply use the move assignment but emits a warning from g++ "warning: reference to local variable 'a' returned", even if the variable goes immediately out of scope.
Already other people found that problem and this is already made a c++ standard language core issue
Interestingly the issue was already found in 2010 but not solved until now...
To give you an answer to your question "In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?" is, that also C++ committee does not have an answer until now. To be precise, there is a proposed solution and this one is accepted but until now not part of the language.
From: Comment Status
Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
consider the below example. I have compiled the sample code using -fno-elide-constructors flag to prevent RVO optimizations:
g++ -fno-elide-constructors -o test test.cpp
#include<iostream>
using namespace std;
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
A a_global(1);
A helper_alt(A a) {
return a;
}
A helper_a_local(A a) {
A x(1);
return x;
}
A helper_a_global(A a) {
return a_global;
}
int main(){
A a1(1);
A a4(4);
std::cout << "================= helper_alt(a1) ==================" << std::endl;
a4 = helper_alt(a1);
std::cout << "=============== helper_a_local() ================" << std::endl;
a4 = helper_a_local(a1);
std::cout << "=============== helper_a_global() ================" << std::endl;
a4 = helper_a_global(a1);
return 0;
}
This will result in the below output:
Def constructor
Def constructor
Def constructor
================= helper_alt(a1) ==================
Copy constructor
Move constructor
Move Assignment
=============== helper_a_local() ================
Copy constructor
Def constructor
Move constructor
Move Assignment
=============== helper_a_global() ================
Copy constructor
Copy constructor
Move Assignment
In simple words, C++ constructs a new temporary object (rvalue) when the return type is not a reference, which results in calling Move or Copy constructor depending on the value category and the lifetime of the returned object.
Anyway, I think the logic behind calling the constructor is that you are not working with reference, and returned identity should be construed first, either by copy or move constructor, depending on the returned value category or lifetime of the return object. As another example:
A helper_move_vs_copy(A a) {
// Call the Copy Constructor
A b = a;
// Call the Move Constructor, Due to the end of 'a' lifetime
return a;
}
int main(){
A a1(1);
A a2(4);
std::cout << "=============== helper_move_vs_copy() ================" << std::endl;
helper_move_vs_copy(a1);
return 0;
}
which outputs:
Def constructor
Def constructor
=============== helper_move_vs_copy() ================
Copy constructor
Copy constructor
Move constructor
From cppreference:
an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
At last, it is the job of RVO to decrease unnecessary moves and copies by optimization of the code, which can even result in an optimized binary for basic programmers!
I'm reading Prata's C++ book, and when talking about copy constructors, it says that this constructor is invoked when:
Initializing a class object to the a class object of the same type.
Passing an object by value to a function.
Returning an object by value from a function.
Let say that we are working with a Vector class.
For understanding sakes, throughout the examples in the current chapter we included in the constructors/destructor definitions string outputs, to indicate which and when each of them is called.
For instance, if we have
int main()
{
Vector v1; // print "Default constructor called"
Vector v2(9, 5); // print "Constructor called"
Vector v3 = v1; // print "Copy Constructor called"
return 0;
}
And Destructor called would be printed in this case 3 times, when exiting main().
In order to check the 3 points above I've been playing with a dumb_display() function, changing the type of the formal parameter/return value. In doing so, I got confused about what is indeed happening under the hood.
Vector dumb_display(Vector v)
{
Vector test(45, 67);
cout << "In dumb_display() function" << endl;
cout << v << endl;
return v;
}
Here:
Every time we return by value the passed argument as in the above function (argument either passed by value or reference) the copy constructor gets called.
It makes sense. It satisfies point 3.
Every time we return an object defined in the function's body (e.g., changing return v; by return test;), the copy constructor isn't called.
Point 3 isn't satisfied.
I'm having a hard time trying to understand this behaviour.
I don't know whether this is correct, but I think (since an automatic storage duration object is created once for the duration of the function call) once test is created it, hasn't have to be created again, because the object "is already there". This brings the question:
Why does returning the passed argument call the copy constructor twice? Why does the same object have to be created twice for the duration of the call to the function?
#include <vector>
#include <type_traits>
#include <tuple>
#include <iostream>
using namespace std;
struct S {
S(){
cout << "default constructor" << endl;
}
S(S const &) {
cout << "copy constructor" << endl;
}
S(S &&) {
cout << "move constructor" << endl;
}
S & operator=(S const &) {
cout << "copy assignment" << endl;
return *this;
}
S & operator=(S &&) {
cout << "move assignment" << endl;
return *this;
}
};
S f() {
S s2;
cout << "In f()" << endl;
return s2;
}
S f2(S s) {
cout << "In f2()" << endl;
return s;
}
int main() {
cout << "about to call f" << endl;
S s2 = f();
(void)s2;
cout << endl << "about to call f2" << endl;
S s3 = f2(s2);
(void)s3;
}
results in:
about to call f
default constructor
In f()
about to call f2
copy constructor
In f2()
move constructor
In f(), the object is default constructed and return value optimization is used to actually construct it in place where it will actually end up -- in the s2 variable in main. No copy/move constructors are called.
In f2(), a copy is made for the input parameter for the function. That value is then moved into the variable s3 in main, again with return return value optimization.
live: https://wandbox.org/permlink/kvBHBJytaIuPj0YN
If you turn off return value optimization, you will see the results that you would expect from what your book says:
live: https://wandbox.org/permlink/BaysuTYJjlJmMGf6
Here's the same two examples without move operators if that's confusing you:
live: https://wandbox.org/permlink/c0brlus92psJtTCf
and without return value optimization:
live: https://wandbox.org/permlink/XSMaBnKTz2aZwgOm
it's the same number of constructor calls, just using the (potentially slower) copy constructor instead of the move constructor.
The copy constructor is called twice because it is first copied from the function into the temperary value (which is represented by the function call and is what the returned value is, then copied into the variable, requiring two copies. Since this is not very efficient, there is also a "move" constructor, which is only needed once.
I'm trying to understand a move constructor,
usually a copy constructor is called when objects are copied
that what happen when I do not provide a move constructor,
but when I add a move constructor it is called instead of my copy constructor,
here is my code:
#include <iostream>
#include <vector>
using namespace std;
struct A
{
A()
{
cout << "A's constructor" << endl;
}
A(const A& rhs)
{
cout << "A's copy constructor" << endl;
}
A(A&& rhs)
{
cout << "A's move constructor" << endl;
}
};
int main() {
vector<A> v;
cout << "==> push_back A():";
v.push_back(A());
cout << "==> push_back A():" << endl;
v.push_back(A());
return 0;
}
does the compiler try to optimize my code and choose the better method ?
Basically, yes.
However, this is not about compiler optimisations as much as it is about how the language itself has been optimised.
Half of the point of move semantics is to allow efficient use of temporaries. That's why temporaries bind to rvalue refs and it's how the entire move semantics thing works. When you don't have a temporary but you wish to move something anyway, you write std::move to obtain an rvalue and trigger this same behaviour.
There is no point copying those temporaries when they are about to be destroyed anyway.
In concert with copy/move elision, this feature will result in much less redundant computing.
Note, however, that your move constructor is not terribly useful — if you ever wanted to make it actually do something (like, um, move stuff) you'd have to remove that const.
rvalue references are better candidates to bind temporaries than const lvalue references. It is not about optimizing (as in compiler optimizations), it is about following standard. The actual optimization would be to not call copy constructor at all.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
#include <vector>
#include <iostream>
#include <iterator>
using namespace std;
class MoveableClass
{
public:
MoveableClass() {
cout << "Default constructor" << endl;
}
MoveableClass(const MoveableClass& src) {
cout << "Copy constructor" << endl;
}
MoveableClass(MoveableClass&& src) {
cout << "Move constructor" << endl;
}
MoveableClass& operator=(const MoveableClass& rhs) {
cout << "Copy assignment operator" << endl;
return *this;
}
MoveableClass& operator=(MoveableClass&& rhs) {
cout << "Move assignment operator" << endl;
return *this;
}
};
int main()
{
vector<MoveableClass> vecSource(3);
cout << "----" << endl;
MoveableClass mc;
cout << "----" << endl;
vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
cout << "----" << endl;
// Copy the elements from vecSource to vecOne
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
cout << "----" << endl;
// Move the elements from vecSource to vecTwo
vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
make_move_iterator(vecSource.end()));
cout << "----" << endl;
return 0;
}
From the above code I have 2 doubts:
Why move ctor is not called from implemented class when I use 2 push_back(mc) functions
call to copy ctor is 3 times i.e 1 for first push and for 2nd push first vector is resized (sequently grow) to different memory location (which should have triggered move for first push) 3rd for 2nd push
Even when I initialize the vector object with size 3 why the call to copy ctor increases to 4 for one push_back(mc).
Output:
Default constructor
Default constructor
Default constructor
----
Default constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Move constructor
Move constructor
Move constructor
Move constructor
----
gcc version I am using is:
> gcc version 4.7.3
UPDATE
Thanks for replies I am getting somewhere
for my 1) point I want to add
// MoveableClass(const MoveableClass& src) {
// cout << "Copy constructor" << endl;
// }
MoveableClass(MoveableClass&& src) noexcept {
cout << "Move constructor" << endl;
}
....
void fun() {
cout << "hello\n";
}
int main()
{
vector<MoveableClass> vecSource(3);
// vector<MoveableClass>::iterator it;
// vecSource.reserve(3);
cout << "----" << endl;
MoveableClass mc;
cout << "----" << endl;
mc.fun();
vecSource.push_back(mc);
// vecSource.push_back(move(mc));
// vecSource.push_back(move_if_noexcept(mc));
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// for(it = vecSource.begin(); it != vecSource.end(); ++it )
// cout << (*it).fun() << endl;
cout << "----" << endl;
// Copy the elements from vecSource to vecOne
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
cout << "----" << endl;
// Move the elements from vecSource to vecTwo
vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
make_move_iterator(vecSource.end()));
cout << "----" << endl;
return 0;
}
I have edited above code
// vecSource.push_back(move(mc)); I can call move ctor only
// vecSource.push_back(move_if_noexcept(mc)); I can call move ctor only
understood..
If I comment copy constructor I am getting compile error
knils#knils-HP:IteratorAdapters$ g++ -g -std=c++0x MoveIterators.cpp
Internal compiler error: Error reporting routines re-entered.
Please submit a full bug report,
with preprocessed source if appropriate.
See for instructions.
Preprocessed source stored into /tmp/ccHhV599.out file, please attach this to your bugreport.
why its giving this error , why Its not using its default copy ctor
for 2) point when I initilize for size 3 , does this mean 3 memory locations are initialized with class instance ?
for(it = vecSource.begin(); it != vecSource.end(); ++it )
cout << (*it).fun() << endl;
I am not able to use above code It gives error
MoveIterators.cpp:48:30: note: mismatched types ‘const _CharT*’ and ‘void’
To add I think here lies the diff for resize an reserve where reserve do not call default ctor and leaves the memory uninitialised.
I think its better to use reserve to some space for vector to an extent we need so than It avoids regular memory swaps. even if it exceeds it will to new location.
UPDATE
for piece of code changes
vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);
The o/p I get is
Copy constructor
Copy constructor
Move constructor
I am confused by the order here.
I am expecting it
Copy constructor
Move constructor
Copy constructor
because for first push It initialize one size(copy) for second it reallocate , so moves the existing memory to new location(move) and the copy the second push in new location(copy)
compiler differs why..
Regards!
vector is resized (sequently grow) to different memory location (which should have triggered move for first push)
std::vector will utilize a move-constructor during a reallocation only if that move-constructor is declared with a noexcept specifier, or if there is no available copy-constructor (see std::move_if_noexcept for more details):
By adding the following minor change:
MoveableClass(MoveableClass&& src) noexcept {
// ~~~~~~~^
cout << "Move constructor" << endl;
}
You will get the output:
Copy constructor
Move constructor
Move constructor
Move constructor
The noexcept specifier tells the std::vector implementation that it can safely apply a move-semantics to its content. Otherwise, you wouldn't have a strong exception safety guarantee, which basically states that the vector remains intact if the reallocation fails due to an exception:
§23.3.6.5 [vector.modifiers]/p1:
Requires: If an exception is thrown other
than by the copy constructor, move constructor, assignment operator, or move assignment operator
of T or by any InputIterator operation there are no effects. If an exception is thrown by the move
constructor of a non-CopyInsertable T, the effects are unspecified.
In addition, the push_back member function will not attempt to move-construct a new element unless its argument can be bound by a non-const rvalue reference - if not, then it falls back to a copy-construction. If you want to move-construct a new element based on the mc instance in a push_back call, you need to pass in an xvalue of mc:
vecSource.push_back(std::move(mc));
// ~~~~~~~~^
Output:
Move constructor
Even when I initialize the vector object with size 3 why the call to copy ctor increases to 4 for one push_back(mc).
The initial capacity of vecSource, that one can query with the .capacity() member function, is presumably set to 3 in your case, which means that any attempt to store more elements results in a need for a reallocation, which requires all the elements already stored in the vector to be copy-constructed to a new memory location.
One can avoid unexpected reallocations by reserving a sufficient amount of storage space prior the push_back calls that follow:
vector<MoveableClass> vecSource;
vecSource.reserve(4); // reserve a storage for 4 elements
vecSource.resize(3); // default-construct 3 elements
cout << "----" << endl;
MoveableClass mc;
vecSource.push_back(mc); // copy-construct 4th element
Output:
Default constructor
Default constructor
Default constructor
----
Default constructor
Copy constructor
2) point when I initilize for size 3 , does this mean 3 memory locations are initialized with class instance ?
Yes, by giving an initial capacity in the vector's constructor call, or by using the resize member function, in C++11 you get that amount of default-constructed (in C++03 - copy-constructed from a default-constructed element) elements that are ready to be accessed and used.
cout << (*it).fun() << endl;
I am not able to use above code It gives error
You can't print out the result of a function call that declares void as a return type. Just remove the cout part and it wll compile:
for(auto it = vecSource.begin(); it != vecSource.end(); ++it )
(*it).fun();
If I comment copy constructor I am getting compile error
Certain operations require the type of vector element to be CopyConstructible; in your code these are:
vecSource.push_back(mc);
//...
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
"Copy constructor Copy constructor Move constructor." I am confused by the order here. I am expecting it "Copy constructor Move constructor Copy constructor"
For the following piece of code:
vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);
According to your output, the following happens:
The initial capacity of vector is set to 0.
First push_back call: the mc is copy-inserted to a newly allocated memory storage (Copy constructor).
Second push_back call: the mc is attempted to be copy-inserted. The capacity of vector is too small, so a new storage is allocated. The copy of mc is inserted into the new storage (Copy constructor). Then, the rest of elements are moved to the new memory location (Move constructor).
I don't think the order in which a copy-construction of an appended element goes before a rellocation is mandated by the Standard, it's just how it's implemented in libstdc++ you're using.
Side notes:
Prefer -std=c++11 to -std=c++0x if the compiler supports the former.
You should not reuse an instance that has already been moved from. I hope you're doing this only for testing purposes.
I have not understood your questions. Sorry. But in my opinion the only place in the code where a question can be arised why is not move constructor used is where you call push_back
vecSource.push_back( mc );
In this place the vector reallocates memory that to accomodate one more element that is a copy of mc. If the vector would use move constructor for its already existent elements when in case of an exceprtion the state of the vector would be undefined. Using the copy constructor guarantees that even in case of an exceprion the state of the vector will be valid because the original elements will not be changed.
But if you will declare the move constructor as not throwing an exception for example
MoveableClass( MoveableClass &&src ) noexcept
{
std::cout << "Move constructor" << std::endl;
}
then the output will be
Copy constructor
Move constructor
Move constructor
Move constructor