I have two pieces of code here to show you. They are two classes and each one provides a Move Constructor and a function which returns a temporary.
- In the first case, the function returning a temporary calls the Move Constructor
- In the second case, the function returning a temporary just tells the compiler to perform a copy elision
I'm confused: in both cases I define a Move Constructor and a random member function returning a temporary. But the behavior changes, and my question is why.
Note that in the following examples, the operator<< was overloaded in order to print a list (in the first case) and the double data member (in the second case).
MOVE CONSTRUCTOR GETS CALLED
template<typename T>
class GList
{
public:
GList() : il{ nullptr } {}
GList(const T& val) : il{ new Link<T>{ val,nullptr } } {}
GList(const GList<T>& copy) {}
GList(GList<T>&& move)
{
std::cout << "[List] Move constructor called" << std::endl;
// ... code ...
}
// HERE IS THE FUNCTION WHICH RETURNS A TEMPORARY!
GList<T> Reverse()
{
GList<T> result;
if (result.il == nullptr)
return *this;
...
...
...
return result;
}
};
int main()
{
GList<int> mylist(1);
mylist.push_head(0);
cout << mylist.Reverse();
return 0;
}
The output is:
[List] Move constructor called
0
1
COPY ELISION PERFORMED
class Notemplate
{
double d;
public:
Notemplate(double val)
{
d = val;
}
Notemplate(Notemplate&& move)
{
cout << "Move Constructor" << endl;
}
Notemplate(const Notemplate& copy)
{
cout << "Copy" << endl;
}
Notemplate Redouble()
{
Notemplate example{ d*2 };
return example;
}
};
int main()
{
Notemplate my{3.14};
cout << my.Redouble();
return 0;
}
The output is:
6.28
I was expecting a call to the Move Constructor in the second example. After all, the logic for the function is the same: return a temporary.
Will someone explain me why that's not happening?
How do I deal with copy elisions?
I want my code to be the most portable I can, how can I be sure of these kinds of optimizations by the compiler?
See Question&Answers more detail:os