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