I try to understand the concept of move semantic and did some tests. I have the following function:
// testing the move semantic when passing
// argument to be modified without copying
void process_copy( std::vector<int>&& aVec)
{
std::cout << "Move semantic\n";
aVec.push_back(42);
}
now in the main function, the following:
int main()
{
std::vector<int> w_vec = {1,2,3,4,5};
process_copy( std::move(w_vec));
}
I expect that the w_vec is now empty since I pass it with move cast (cast lvalue to rvalue). But the result is w_vec contains 6 elements now (42 has been added to the vector).
Something that I miss? Is std::vector is a moveable object?
An image of my CoreCpp 2019 t-shirt might be relevant here.
This is the front.
It's interesting to note that this behavior of std::move was a bit surprising even for Howard Hinnant himself, the man behind rvalue and move semantics. However he got a very thorough answer there.
What you may be missing is that you are just binding an rvalue reference to the vector you pass; you are moving no vector object at all.
The process_copy() parameter is of type std::vector<int>&&, i.e., an rvalue reference to std::vetor<int>:
void process_copy(std::vector<int>&& aVec)
By using std::move() when calling process_copy() as in:
process_copy(std::move(w_vec));
You are just making it possible to bind this reference – i.e., process_copy()'s parameter, aVec – to the argument – i.e., the vector w_vec. That is, the following does not compile:
process_copy(w_vec);
Because you can't bind an rvalue reference (aVec) to an lvalue (w_vec).
If you want the vector argument w_vec to be moved when calling process_copy(), then you could have the function to take an std::vector<int> by value instead and move construct this parameter at the moment of calling the function:
void process_copy(std::vector<int> aVec) // <-- not a reference
{
aVec.push_back(42);
}
By marking the vector argument with std::move() when calling this process_copy(), the parameter aVec will be move constructed – so the vector argument will end up in a moved-from state.
Related
in a class with the following member and method:
std::unordered_map<std::string, std::unique_ptr<test::Test>> m_Tests;
void test::TestMenu::addTest(const std::string &testName, std::unique_ptr<test::Test> test) {
m_Tests.emplace(testName, test);
}
called like this:
testMenu.addTest("Test Clear Color", std::make_unique<test::TestClearColor>());
The compiler delivers the error:
error: no matching function for call to 'construct_at'
std::construct_at(__p, std::forward<_Args>(__args)...);
My question is why is C++ giving me this error?
This:
m_Tests.emplace(testName, test);
will try to copy test, because (a) it is an lvalue expression, (b) expressions never have reference type, and (c) emplace does not take an lvalue reference.
unique_ptrs cannot be copied.
This is what std::move is for!
m_Tests.emplace(testName, std::move(test));
Now you have an rvalue expression, which is perfectly suited to bind to the rvalue reference that emplace does take.
I'm actually slightly slightly lying here, and things are a bit more complicated since emplace is variadic. But whatever. The important thing is that you're creating another link in the chain of ownership for this unique_ptr, ultimately passing ownership on to the map, and std::move is the way to do that.
I am learning CPP++14 move semantics.While writing a small code I observed some weird behavior. I am moving vector of unique ptr to a function using r-value refrence. on debuuging I found that the changes are being applied to the moved object also. Why am I observing this hcnage even the object is moved? Whats does the move do in following code?
void func(std::vector<std::unique_ptr<int>> && vect) {
vect.emplace_back(std::move(std::make_unique<int>(3)));
return ;
}
int main() {
std::vector<std::unique_ptr<int>> a;
func(std::move(a));
cout<<(*(a[0]))<<endl;
return 0;
}
Whats does the move do in following code?
Move operation is not performed in func(std::move(a)); in fact, std::move just performs conversion and produces an rvalue (xvalue) expression, which is just bound to the rvalue reference parameter vect of func. Then any modification on vect inside func has effect on the argument (i.e. a) too, they refer to the same object.
In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.
If you change the parameter to pass-by-value, then you'll see move operation is performed. And given the usage you showed, just pass-by-lvalue-reference seems less confusing (and no need to use std::move on argument again).
BTW: In vect.emplace_back(std::move(std::make_unique<int>(3))); the usage of std::move is superfluous, std::make_unique<int>(3) been an rvalue expression.
I am trying to understand rvalue reference and move semantics. In following code, when I pass 10 to Print function it calls rvalue reference overload, which is expected. But what exactly happens, where will that 10 get copied (or from where it referred). Secondly what does std::move actually do? Does it extract value 10 from i and then pass it? Or it is instruction to compiler to use rvalue reference?
void Print(int& i)
{
cout<<"L Value reference "<<endl;
}
void Print(int&& i)
{
cout<<"R Value reference "<< endl;
}
int main()
{
int i = 10;
Print(i); //OK, understandable
Print(10); //will 10 is not getting copied? So where it will stored
Print(std::move(i)); //what does move exactly do
return 0;
}
Thanks.
In the case of a 10, there will probably be optimisations involved which will change the actual implementation, but conceptually, the following happens:
A temporary int is created and initialised with the value 10.
That temporary int is bound to the r-value reference function parameter.
So conceptually, there's no copying - the reference will refer to the temporary.
As for std::move(): there may be some tricky bits related to references etc., but principally, it's just a cast to r-value reference. std::move() does not actually move anything. It just turns its argument into an r-value, so that it can be moved from.
"Moving" is not really a defined operation, anyway. While it's convenient to think about moving, the important thing is l-value vs. r-value distinction.
"Moving" is normally implemented by move constructors, move assignment operators and functions taking r-value references (such as push_back()). It is their implementation that makes the move an actual move - that is, they are implemented so that they can "steal" the r-value's resources instead of copying them. That's because, being an r-value, it will no longer be accessible (or so you promise the compiler).
That's why std::move() enables "moving" - it turns its argument into an r-value, signalling, "hey, compiler, I will not be using this l-value any more, you can let functions (such as move ctors) treat it as an r-value and steal from it."
But what exactly happens, where that 10 will get copied (or from where it referred)
A temporary value is created, and a reference passed to the function. Temporaries are rvalues, so can be bound to rvalue references; so the second overload is chosen.
Secondly what std::move actually do?
It gives you an rvalue reference to its argument. It's equivalent (by definition) to static_cast<T&&>.
Despite the name, it doesn't do any movement itself; it just gives you a reference that can be used to move the value.
std::move cast int in int&& via static_cast<int&&>.
Eventually if the type is a class or a struct, the move constructor if it is defined (implicitly or explicitly) will be invoked instead of the copy constructor/classical constructor.
std::shared_ptr::operator* returns by lvalue reference, and the answer given on overloading pointer like operations here says that the convention is to return by lvalue reference. However, when I'm using the following code, I get error C2664: 'AdjacencyList::addVertex' : cannot convert parameter 1 from 'AdjacencyList::vertex_type' to 'AdjacencyList::vertex_type &&': You cannot bind an lvalue to an rvalue reference:
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type&& v)
{
auto existingVertex(findVertex(v));
if (!existingVertex.isValid())
{
existingVertex = std::make_shared<vertex_type>(std::forward<vertex_type>(v))
m_vertices.push_back(existingVertex);
}
return existingVertex;
};
AdjacencyList minimumSpanningTree;
// startVertex is a shared_ptr to a vertex returned from a previous call of addVertex
// on another AdjacencyList object
const auto mstStartVertex(minimumSpanningTree.addVertex(*startVertex));
Should I provide AdjacencyList::addVertex(const vertex_type& v) or change the code at the bottom of the above block to make a copy of the vertex before passing to addVertex?
AdjacencyList minimumSpanningTree;
Vertex s(*startVertex);
const auto mstStartVertex(minimumSpanningTree.addVertex(std::move(s)));
I would think that you should return a copy from your operator*, as the sematics of the std::weak_ptr suggest that you can not guarantee that a returned reference would stay valid. Since the returned copy is then given to a function which can move it somewhere else, it should also be efficient enough, since addVertex looks like it would require a copy anyways, i.e., if you would create an overload of addVertex, it will create a copy of the passed const reference internally, would it?
The most efficient approach in terms of redundant copies is to provide rvalue and const reference overloads:
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type&&);
std::shared_ptr<vertex_type> AdjacencyList::addVertex(const vertex_type&);
To eliminate the redundant code, you can forward to a template method or to a concrete method taking a bool flag and performing const_cast as appropriate.
If the overhead of copying the Vertex object is minimal compared to the cost of increased code, and if the if block is usually or often entered, then the redundant copy will make your code clearer. Your second suggested call will work better if you just create a prvalue temporary that doesn't need to be moved:
const auto mstStartVertex(minimumSpanningTree.addVertex(Vertex{*startVertex}));
However in that case you might as well create the temporary in the call itself, by providing a single value overload (How to reduce redundant code when adding new c++0x rvalue reference operator overloads):
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type);
I would like to implement a function that fills up a vector and then returns an rvalue reference. I tired something like:
std::vector<int> &&fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
return 0;
}
but that doesn't work, I get the following error:
error: invalid initialization of reference of type 'std::vector<int>&&' from expression of type 'std::vector<int>'
So, all in all, how is the right way of doing it? I don't think I get rvalue references just yet.
You seem to be confused as to what an rvalue reference is and how it relates to move semantics.
First thing's first: && does not mean move. It is nothing more than a special reference type. It is still a reference. It is not a value; it is not a moved value; it is a reference to a value. Which means it has all of the limitations of a reference type. Notably, it must refer to a value that still exists. So returning a dangling r-value reference is no better than returning a dangling l-value reference.
"Moving" is the process of having one object claim ownership of the contents of another object. R-value references facilitate move semantics, but simply having a && does not mean anything has moved. Movement only happens when a move constructor (or move assignment operator) is called; unless one of those two things is called, no movement has occurred.
If you wish to move the contents of a std::vector out of your function to the user, you simply do this:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
Given this usage of fill_list():
std::vector<int> myvec = fill_list();
One of two things will happen. Either the return will be elided, which means that no copying or moving happens. res is constructed directly into myvec. Or res will be moved into the return value, which will then perform move-initialization of myvec. So again, no copying.
If you had this:
std::vector<int> myvec;
myvec = fill_list();
Then again, it would be moved into. No copying.
C++11 knows when it's safe to implicitly move things. Returning a value by value rather than by reference or something is always a safe time to move. Therefore, it will move.
The return statement is an error because you atempt to bind an rvalue reference (the return type) to an lvalue (the vector res). An rvalue reference can only be bound to an rvalue.
Also, as others already mentioned, returning a local variable when the return type is a reference type is dangerous, because the local object will be destroyed after the return statement, then you get a reference that refers to an invalid object.
If you want to avoid the copy construction during the return statement, just using a non-reference type might already works due to a feature called copy elision. the vector res in your fill_list function may be directly constructed into the vector myvec in your main function, so no copy or move construction is invoked at all. But this feature is allowed by Standard not required, some copy construction is not omitted in some compiler.
For a discussion of rvalue references you can read what Bjarne Stroustrup, the author of C++, has to say about them here:
http://www2.research.att.com/~bs/C++0xFAQ.html#rval
Addressing your specific example, the short answer is that due to the Named Return Value Optimization - which is a de facto standard C++ compiler feature even pre-C++11 - if you simply return-by-value the compiler will tie up res and myvec efficiently like you want:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
cout << &res << endl; // PRINT POINTER
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
cout << &myvec << endl; // PRINT POINTER
return 0;
}
The two "PRINT POINTER" lines will print the same pointer in both cases.
The vector myvec and the vector res in the above will be the same vector with the same storage. No copy constructor or move constructor will be called. In a sense res and myvec will be two aliases of the same object.
This is even better than using a move constructor. myvec is constructed and "filled up" in-place.
The compiler achieves this by compiling the function in an "inplace" mode overlaying an immediate stack position in the callers stack frame with the callees local result variable, and simply leaving it there after the callee returns.
In this circumstance we say that the constructor has been elided. For more information see here:
http://en.wikipedia.org/wiki/Return_value_optimization
In the event that you were assigning the result of fill_list in a non-constructor context, than as a return-by-value results in an xvalue (short for "expiring" value, a type of rvalue), the move assignment operator of the target variable would be given preference if it is available.
If you just remove the && from your function it should work, but it will not be a reference. fill_list() will create a vector and return it. During the return a new vector will be created. The first vector that was created inside fill_list() will be copied to the new vector and then will be destroyed. This is the copy constructor's work.