Lately, I have been reading this post and that post suggesting to stop returning const objects. This suggestion is also given by Stephan T. Lavavej in his talk in Going Native 2013.
I wrote a very simple test to help me understand which constructor/operator is called in all those cases:
- Returning const or non const objects
- What if Return Value Optimization (RVO) kicks in ?
- What if Named Return Value Optimization (NRVO) kicks in ?
Here is the test:
#include <iostream>
void println(const std::string&s){
    try{std::cout<<s<<std::endl;}
    catch(...){}}
class A{
public:
    int m;
    A():m(0){println("    Default Constructor");}
    A(const A&a):m(a.m){println("    Copy Constructor");}
    A(A&&a):m(a.m){println("    Move Constructor");}
    const A&operator=(const A&a){m=a.m;println("    Copy Operator");return*this;}
    const A&operator=(A&&a){m=a.m;println("    Move Operator");return*this;}
    ~A(){println("    Destructor");}
};
A nrvo(){
    A nrvo;
    nrvo.m=17;
    return nrvo;}
const A cnrvo(){
    A nrvo;
    nrvo.m=17;
    return nrvo;}
A rvo(){
    return A();}
const A crvo(){
    return A();}
A sum(const A&l,const A&r){
    if(l.m==0){return r;}
    if(r.m==0){return l;}
    A sum;
    sum.m=l.m+r.m;
    return sum;}
const A csum(const A&l,const A&r){
    if(l.m==0){return r;}
    if(r.m==0){return l;}
    A sum;
    sum.m=l.m+r.m;
    return sum;}
int main(){
    println("build a");A a;a.m=12;
    println("build b");A b;b.m=5;
    println("Constructor nrvo");A anrvo=nrvo();
    println("Constructor cnrvo");A acnrvo=cnrvo();
    println("Constructor rvo");A arvo=rvo();
    println("Constructor crvo");A acrvo=crvo();
    println("Constructor sum");A asum=sum(a,b);
    println("Constructor csum");A acsum=csum(a,b);
    println("Affectation nrvo");a=nrvo();
    println("Affectation cnrvo");a=cnrvo();
    println("Affectation rvo");a=rvo();
    println("Affectation crvo");a=crvo();
    println("Affectation sum");a=sum(a,b);
    println("Affectation csum");a=csum(a,b);
    println("Done");
    return 0;
}
And Here is the output in release mode (with NRVO and RVO):
build a
    Default Constructor
build b
    Default Constructor
Constructor nrvo
    Default Constructor
Constructor cnrvo
    Default Constructor
Constructor rvo
    Default Constructor
Constructor crvo
    Default Constructor
Constructor sum
    Default Constructor
    Move Constructor
    Destructor
Constructor csum
    Default Constructor
    Move Constructor
    Destructor
Affectation nrvo
    Default Constructor
    Move Operator
    Destructor
Affectation cnrvo
    Default Constructor
    Copy Operator
    Destructor
Affectation rvo
    Default Constructor
    Move Operator
    Destructor
Affectation crvo
    Default Constructor
    Copy Operator
    Destructor
Affectation sum
    Copy Constructor
    Move Operator
    Destructor
Affectation csum
    Default Constructor
    Move Constructor
    Destructor
    Copy Operator
    Destructor
Done
    Destructor
    Destructor
    Destructor
    Destructor
    Destructor
    Destructor
    Destructor
    Destructor
What I don't understant is this: why is the move constructor used in the "Constructor csum" test ?
The return object is const so I really feel like it should call the copy constructor.
What am I missing here ?
It should not be a bug from the compiler, both Visual Studio and clang give the same output.
 
     
     
     
    