Reputation: 35
I'm reading from here: http://www.cplusplus.com/reference/deque/deque/push_back/
for
void push_back (const value_type& val);
that val is a
Value to be copied (or moved) to the new element.
e.g. if val is an instance of a class, in which case does it get copied and in which it gets moved? What is the difference between the two cases?
Can you also please explain what different happens in each one of the following add_val1, add_val2 and add_val3 ?
#include <deque>
class Value
{
int member1;
int member2;
};
class ValuesContainer
{
private:
std::deque<Value> m_vals;
public:
void add_val1(const Value &val)
{
m_vals.push_back( val );
}
void add_val2(const Value val)
{
m_vals.push_back( val );
}
void add_val3(const Value &val)
{
m_vals.push_back( Value(val) );
}
};
int main()
{
return 0;
}
Thanks!
Upvotes: 1
Views: 3105
Reputation: 313
if val is an instance of a class, in which case does it get copied and in which it gets moved? What is the difference between the two cases?
If a value is an instance of a class, let's say for example
class MyValue {};
MyValue originalValue;
MyValue copyAssignedValue = originalValue;
It always gets copied. This happens because the copy assignment operator gets called. In a similar matter,
MyValue copyConstructedValue(originalValue);
calls the copy constructor (which, as the name indicates, copies the content).
To understand, why and when copy or move constructors are used, it is necessary to understand the difference between the two operations:
Copying an object is like a normal Copy and Paste: You now have two copies of the same object that you can work on independently. Moving an object is like Cut and Paste: You only have one working object in the end, and the old one gets deleted. It is, however, much faster then copying because you don't need to allocate new memory.
To move an object, you can do a few things. For example, you can create an object that the compiler knows you'll only need in the move constructor.
MyValue moveConstructedValue(MyValue());
Just to be clear, this is what a move constructor looks like:
MyValue::MyValue(MyValue&& other);
Note the double &&
, which indicates a rvalue reference (single &
are now called lvalue references).
Or you can indicate that you want to use the move constructor explicitly (be carefull, the originValue object will be reseted to default values):
MyValue valueToBeMovedTo(std::move(originalValue));
To answer your second question, which of your functions uses a move operation: Actually, none of them "move" the val into m_vals, because the compiler doesn't know if you want to use it afterwards. You could use moving though by using the new rvalue reference:
void add_valMoving(const Value &&val)
{
m_vals.push_back( val );
}
To 100% correct, add_val3 uses a move constructor, but not directly on the val object: It first calls the copy constructor of Value and copies val into a new object, and then uses the move constructor to move the new object into the deque.
Edit:
To be more clear on the "default values" when moving objects, a move constructor could be:
class MyValue
{
public:
MyValue(MyValue&& other)
{
this->member = other.member;
other.member = 0;
}
private:
int member;
}
You never know, however, to what values the original object will be set after a move.
Upvotes: 1
Reputation: 62576
That reference is confusing you, because it is by default showing only the copying push_back
, and still talking about moving.
Since C++11, there are two push_back
s
void push_back( const T& value );
which always copiesvoid push_back( T&& value );
which always movesIf the value you are pushing back has a name, or is explicitly a T&
, then it will use the first, otherwise the second
void add_val1(const Value &val)
{
m_vals.push_back( val ); // copy
}
void add_val2(const Value val)
{
m_vals.push_back( val ); // copy
}
void add_val3(const Value &val)
{
m_vals.push_back( Value(val) ); // move the temporary that is a copy of val
}
extern Value make_value_somehow()
void add_val4()
{
m_vals.push_back( make_value_somehow() ); // move
}
extern Value& find_value_somehow()
void add_val5()
{
m_vals.push_back( find_value_somehow() ); // copy
}
void add_val6(Value val)
{
m_vals.push_back( val ); // copy
}
void add_val7(Value val)
{
m_vals.push_back( std::move(val) ); // move
}
void add_val8(const Value val)
{
// m_vals.push_back( std::move(val) ); // ill-formed, can't modify val
}
void add_val9(Value & val)
{
m_vals.push_back( std::move(val) ); // move, which will confuse people
}
Upvotes: 1
Reputation: 5209
Value to be copied (or moved) to the new element.
This depends on overload used in push_back
(see this, in my opinion, better reference page).
add_val1
takes reference and push_back
copies that.
add_val2
takes value (so the function will copy it) and than it's copied again by push_back
.
add_val3
takes reference, you invoke a copy, but since Value(val)
is r-value, the temporary is then moved by push_back
.
Upvotes: 0