When returning an object from a function one of the following cases can happen since C++11, assuming move- and copy-constructors are defined (see also the examples at the end of this post):
- it qualifies for copy-elision and compiler performs RVO.
- it qualifies for copy-elision and compiler doesn't perform RVO, but then ...
- it qualifies for the usage of move constructor and is moved.
- none of the above and the copy-constructor is used.
The advice for the first 3 cases is not to use the explicit std::move
, because move is performed anyway and could prevent possible RVO, see for example this SO-post.
However, in the 4. case an explicit std::move
would improve the performance. But as somebody who is fluent in reading neither the standard nor the resulting assembler, it takes a lot of time to differentiate between cases 1-3 and 4.
Thus my question: Is there are a way to handle all of the above cases in an unified manner, such that:
- RVO is not impeded (case 1)
- if RVO is not performed, move-constructor is used (cases 2,3 and 4)
- if there is no move-constructor, copy-constructor should be used as fallback.
Here are some examples, which also can be used as test cases.
All examples use the following helper-class-definition:
struct A{
int x;
A(int x_);
A(const A& a);
A(const A&& a);
~A();
};
1. example: 1.case, RVO performed, live-demonstration, resulting assembler:
A callee1(){
A a(0);
return a;
}
2. example: 1.case, RVO performed, live-demonstration, resulting assembler:
A callee2(bool which){
return which? A(0) : A(1);
}
3. example: 2.case, qualifies for copy-elision, RVO not performed, live-demonstration, resulting assembler:
A callee3(bool which){
A a(0);
A b(1);
if(which)
return a;
else
return b;
}
4. example: 3.case, doesn't qualify for copy-elision (x
is a function-parameter), but for moving, live-demonstration, resulting assembler:
A callee4(A x){
return x;
}
5. example: 4.case, no copy-elision or implicit moving (see this SO-post), live-demonstration, resulting assembler:
A callee5(bool which){
A a(0);
A b(1);
return which ? a : b;
}
6. example: 4.case, no copy-elision or implicit moving, live-demonstration, resulting assembler:
A callee6(){
std::pair<A,int> x{0,1};
return x.first;
}
Aucun commentaire:
Enregistrer un commentaire