vector move operation vs element move operation - c++

For the following example, why the vector move operation is not triggered? How do I know when I should explicitly use a move operator?
#include <iostream>
#include <vector>
using namespace std;
class Test {
public:
Test() {
std::cout << " default " << std::endl;
}
Test(const Test& o) {
std::cout << " copy ctor " << std::endl;
}
Test& operator=(const Test& o) {
std::cout << " copy assign " << std::endl;
return *this;
}
Test(Test&& o) {
std::cout << " move ctor" << std::endl;
}
Test& operator=(Test&& o) {
std::cout << " move assign " << std::endl;
return *this;
}
};
int main()
{
std::cout << " vector: " << std::endl;
std::vector<Test> p;
p = {Test()}; // expect vector move here since the RHS is temporary.
std::cout << std::endl;
std::cout << " single value " << std::endl;
Test tt;
tt = Test();
}
Output:
vector:
default
copy ctor
single value
default
default
move assign
I was under the impression that when we assign a temporary variable to a lvalue (the single value case in the example), it would trigger a move operation if it exists. Seems that I was wrong and my understanding was overly simplified, I need to carefully check case by case to ensure there's no redundant copy.

std::vector has an assignment operator that takes an std::initializer_list:
vector& operator= (initializer_list<value_type> il);
So when you wrote p = {Test()}; you're actually using the above assignment operator.
Now why a call to the copy constructor is made can be understood from dcl.init.list, which states:
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array.

Related

push_back is more efficient than emplace_back?

I wanted to see the difference between push_back and emplace_back, as in several places I read recommendation as now it's better to use emplace_back as "it can do all push_back can do and more", so I expect ti to be more efficient. But to my surprise
#include <iostream>
#include <vector>
class A
{
public:
A() {std::cout << "A const" << std::endl;}
~A() {std::cout << "A dest" << std::endl;}
A(const A& a) {std::cout << "A copy const" << std::endl;}
A(A&& a) {std::cout << "A move const" << std::endl;}
A& operator=(const A& a) {std::cout << "A copy operator=" << std::endl; return *this; }
A& operator=(A&& a) {std::cout << "A move operator=" << std::endl; return *this; }
};
int main () {
std::vector<A> va;
std::cout <<"push:" << std::endl;
va.push_back(A());
std::cout <<std::endl<< "emplace:" << std::endl;
va.emplace_back(A());
std::cout <<std::endl<< "end:" << std::endl;
return 0;
}
Output is
push:
A const
A move const
A dest
emplace:
A const
A move const
A copy const
A dest
A dest
end:
A dest
A dest
emplace_back calls move constructor and then copy one when push_back calls only one move const. I checked with g++ (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0 and online C++ shell.
Am I missing something?
push_back is not more efficient, and the results you observe are due to the vector resizing itself.
When you call emplace after push_back, the vector has to resize itself to make room for the second element. This means that it has to move the A that was originally inside the vector, making emplace appear more complex.
If you reserve enough space in the vector beforehand, this doesn't happen. Notice the call to va.reserve(2) after va's creation:
#include <iostream>
#include <vector>
class A
{
public:
A() {std::cout << "A const" << std::endl;}
~A() {std::cout << "A dest" << std::endl;}
A(const A& a) {std::cout << "A copy const" << std::endl;}
A(A&& a) {std::cout << "A move const" << std::endl;}
A& operator=(const A& a) {std::cout << "A copy operator=" << std::endl; return *this; }
A& operator=(A&& a) {std::cout << "A move operator=" << std::endl; return *this; }
};
int main () {
std::vector<A> va;
// Now there's enough room for two elements
va.reserve(2);
std::cout <<"push:" << std::endl;
va.push_back(A());
std::cout <<std::endl<< "emplace:" << std::endl;
va.emplace_back(A());
std::cout <<std::endl<< "end:" << std::endl;
return 0;
}
The corresponding output is:
push:
A const
A move const
A dest
emplace:
A const
A move const
A dest
end:
A dest
A dest
Can we make things even more efficient? Yes! emplace_back takes whatever arguments you provide it, and forwards them to A's constructor. Because A has a constructor that takes no arguments, you can also use emplace_back with no arguments! In other words, we change
va.emplace_back(A());
to
va.emplace_back(); // No arguments necessary since A is default-constructed
This results in no copy, and no move:
push:
A const
A move const
A dest
emplace:
A const
end:
A dest
A dest
A note on vectors resizing: It's important to note that the implementation of std::vector is smart. If A had been a trivially copyable type, std::vector might have been able resize in-place without additional copying using a system function similar to realloc. However because As constructors and destruction contain code, realloc can't be used here.

Change constructor order of operations?

I have the following sample code, and the copy assignment is doing something I don't want - it's first constructing the new samp(6), then copying it to z, then destroying the new samp(6) it built. Is there a way to change the constructors such that = acts like a pointer, destroying the originally constructed samp(5) and replacing it with the new samp(6), calling the destructor on samp(5), rather than samp(6)?
#include <iostream>
class samp
{
public:
samp(int a)
{
m_a = a;
std::cout << "cons" <<m_a << std::endl;
}
int m_a;
samp(const samp& other)
{
std::cout << "copy" << m_a << std::endl;
m_a = other.m_a;
}
samp& operator= (const samp& other)
{
std::cout << "assg" << m_a << std::endl;
samp* z =new samp(other.m_a);
return *z;
}
~samp()
{
std::cout << "dest" <<m_a<< std::endl;
}
};
int main()
{
samp z(5);
z = samp(6);
std::cout << z.m_a << std::endl;
return 0;
}
Maybe pointer semantics is what you want:
#include <memory>
// ...
auto z = std::make_unique<samp>(5);
z = std::make_unique<samp>(6); // dest5
std::cout << z->m_a << '\n'; // 6
Although if you are coming to C++ from a language where object names are object references, it may be better to get used to C++ value semantics instead of trying to replicate object references :)
operator= is a member of your object so the this pointer is available. Also, assignment means the target of the assignment should change. Your version is creating a new object, but leaving the target alone. See if this does what you want:
samp& operator= (const samp& other)
{
m_a = other.m_a;
return *this;
}

How values of member variables are getting copied even though copy constructor is not getting called due to RVO in C++

I am not able to understand how the values of member variables are getting copied even though the constructor is not getting called in the below program.
#include <iostream>
using namespace std;
class myclass
{
public:
int x;
int y;
myclass(int a, int b)
{
cout << "In Constructor" << endl;
x = a;
y = b;
}
~myclass()
{
cout << "In Destructor" << endl;
}
myclass(const myclass &obj)
{
cout << "In Copy Constuctor " << obj.x << " " << obj.y << endl;
x = obj.x;
y = obj.y;
}
myclass &operator=(const myclass &obj)
{
cout << "In Operator Overloading" << obj.x << obj.y << endl;
x = obj.x;
y = obj.y;
return *this;
}
};
int main()
{
myclass obj1 = myclass(2, 3);
cout << "obj1.x : " << obj1.x << "obj1.y" << obj1.y << endl;
}
Output:
In Constructor
obj1.x : 2obj1.y3
In Destructor
I understood that due to Return Value Optimization, copy constructor is not getting called. But I didn't understand how obj1 is getting values 2 and 3. Can any one please help me to understand this or how Return Value Optimization will work behind the scenes.
The values aren't getting copied, because they don't need to be copied. Instead, the values which would have been copied are initialized in place. Copy elision means that the compiler essentially turns this:
myclass obj1 = myclass(2, 3);
Into this:
myclass obj1(2, 3);
So no extra object is constructed which needs to be copied.
Note that RVO, which you've referred to, is a form of copy elision. But this specific case of copy elision is not RVO.

Perfect forwarding and the scope of temporary objects

In an attempt to realize perfect forwarding for the * operator I constructed the following example.
#include <string>
#include <iostream>
class A {
public:
std::string name;
A(const A& _other) : name(_other.name) {
std::cout << "Copy-Construct with name: " << name << std::endl;
}
A(A&& _other) : name(std::move(_other.name)) {
std::cout << "Move-Construct with name: " << name << std::endl;
}
A(std::string _name): name(_name) { }
};
A operator*(const A& _lhs, const A& _rhs) {
std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
A bla(_lhs.name+" "+_rhs.name);
return bla;
}
A&& operator*(A&& _lhs, const A& _rhs) {
std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
_lhs.name += " "+_rhs.name;
return std::move(_lhs);
}
int main() {
A a("a");
A b("b");
A c("c");
A d("d");
A x = a*b*A("t1")*c*A("t2")*A("t3")*d;
std::cout << "Final result is: " << x.name << std::endl;
}
The result is as I hoped, in particular only one move constructor and no copy constructor is called.
Start Operator Copy with: a b
Start Operator Move with: a b t1
Start Operator Move with: a b t1 c
Start Operator Move with: a b t1 c t2
Start Operator Move with: a b t1 c t2 t3
Start Operator Move with: a b t1 c t2 t3 d
Move-Construct with name: a b t1 c t2 t3 d
Final result is: a b t1 c t2 t3 d
Now my question is: Is this leagal C++11 code? In particular can I rely on the fact that the first temporary object (contructed from a and b) leaves its scope at the semicolon and not before that? And is the construction, to return an object obtained as a move reference back as a move reference, legal at all?
A&& operator*(const A& _lhs, const A& _rhs) {
std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
A* bla = new A(_lhs.name+" "+_rhs.name);
return std::move(*bla);
}
This creates a dynamically allocated object, so the caller is responsible for deleting it. Your example fails to do that, so leaks memory. This is a terrible function. It should return by value instead, which will be faster because you don't allocate the object on the heap.
A&& operator*(A&& _lhs, const A& _rhs) {
std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
_lhs.name += " "+_rhs.name;
return std::move(_lhs);
}
This doesn't lead to memory leaks, so isn't totally obviously wrong like the first one, but it's still wrong. If you call it with a temporary object it returns a reference to that same temporary, but that can lead to dangling references:
A&& c = A("a") * A("b");
The reference c is bound to the temporary created by A("a") but that does out of scope at the end of the statement. Any attempt to use c has undefined behaviour.
Both overloads should return by value.
You might also want an overload for the case where the left-hand side is an lvalue and the right-hand side is an rvalue, as that allows you to re-use the right-hand side object. And if you add that you also need an overload for the case where both operands are rvalues. Basically, look at how std::string defines operator+

C++: STL: vector: remove: destructor calls

The code is following:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {
wcout << "Constructor: i = " << i_ << endl;
}
A(A const &a) {
wcout << "Copy constructor: i = " << i_ << " a.i = " << a.i_ << endl;
*this = a;
}
~A() { wcout << "Destructor: i = " << i_ << endl; }
A &operator=(A const& a) {
wcout << "Copy assignment operator: i = " << i_ << " a.i = " << a.i_ << endl;
i_ = a.i_;
return *this;
}
bool operator==(A const& rhs) { return i_ == rhs.i_; }
int get() { return i_; }
private:
int i_;
};
int wmain() {
A a[] = {1, 2, 3, 2, 4, 5};
vector<A> v(a, a + sizeof a/sizeof a[0]);
wcout << "==== Just before remove ====" << endl;
remove(v.begin(), v.end(), 2);
wcout << "==== Just after remove ====" << endl;
return 0;
}
Output:
==== Just before remove ====
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 3
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 3 a.i = 4
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 5
==== Just after remove ====
The question is: why destructor is called 6 times while remove() was running? I need this mess to be clarified.
NOTE: execute this code on your system, please, before answering.
NOTE: MSVCPP 11
The question is: why destructor is called 6 times while remove() was
running?
In summary, the destructor calls have to do with the 2 getting implicitly converted to A by remove(). Every time the result of such an implicit conversion goes out of scope, A's destructor gets called.
The reason for those implicit conversions is that remove() needs to compare every element of a to 2. The only way to do this is by calling A::operator==(const A&):
bool operator==(A const& rhs) { ... }
Since rhs is of type const A&, the compiler:
calls A(int) to convert the 2 to an A;
calls operator==(const A&);
calls A::~A() to destroy the temporary.
The latter are the destructor calls that you're seeing.
If you were to add the following comparison operator to A, you'll see those destructor calls disappear:
bool operator==(int rhs) { return i_ == rhs; }
Alternatively, if you were to call remove() like so, you'll see all bar one destructor calls disappear:
remove( v.begin(), v.end(), A(2) );
Finally, if you were to make A::A(int) explicit, the compiler would not allow you to call remove() with 2 as the last argument (you'd have to call it with A(2)).
The question is: why destructor is called 6 times while remove() was
running?
Because std::remove just reorders the elements, but doesn't remove anything from the vector. In the process of reordering, some elements are copy-constructed.
Next code shows in details what happens :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {cout << "I was created" << i_ << endl;}
~A() {
cout << "I was destroyed" << i_ << endl;
}
A(const A& o):i_(o.i_)
{
cout << "I was copied" << i_ << endl;
}
A& operator=(const A& o)
{
i_=o.i_;
cout << "I was assigned" << i_ << endl;
return *this;
}
int get() { return i_; }
bool operator==(A const& rhs) { return i_ == rhs.i_; }
private:
int i_;
};
int main() {
std::cout<<"start"<<std::endl;
A a[] = {1, 2, 3, 2, 4, 5};
std::cout<<"creating"<<std::endl;
vector<A> v(a, a + sizeof a/sizeof a[0]);
std::cout<<"removing"<<std::endl;
remove( v.begin(), v.end(), 2 );
std::cout<<"end"<<std::endl;
}
The remove is placing "removed" elements at the end of the vector.
std::remove is declared as
template<typename ForwardIterator, typename Tp>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const Tp& value);
In your usage, the third argument (2) is deduced as int. Because an int variable is not directly comparable against an A object, first a temporary has to be constructed for every
if (*it == value) ...
At the end of the comparison, the temporary is destroyed.
(All in all, you are having a hidden performance problem, resulting from having non-explicit single-argument, a.k.a. conversion constructor.)
Well it's quite simple really; you only need to understand what std::remove does and how it does it. Hint: it does not remove elements from the vector. It moves then around instead, to the back of your collection. Moving an element in vector involves destroying original element. So this is where (part of) your destructor calls is coming from.
The other part is coming from temporary objects - since you passed an int (and not an instance of struct A) as a last parameter to std::remove, an instance of A has to be constructed for the purpose of comparison. If you want to force a little more discipline to your code, try to make it a habit to prefix one-parameter constructors with keyword explicit. It's very efficient at banishing such temporary objects. You will then have to create comparison object explicitly:
remove(v.begin(), v.end(), A(2));