What I don't understand is this: why is the move constructor used in the "Constructor csum" test ?
In this particular case the compiler is allowed to do [N]RVO, but it did not do it. The second best thing is to move-construct the returned object.
The return object is const so I really feel like it should call the copy constructor.
That does not matter at all. But I guess that is not completely obvious, so lets walk through what it conceptually mean to return a value, and what [N]RVO is. For that, the simplest approach is to ignore the returned object:
T f() {
T obj;
return obj; // [1] Alternatively: return T();
}
void g() {
f(); // ignore the value
}
This in the line marked as [1] there is a copy from the local/temporary object to the returned value. Even if the value is completely ignored. That is what you are exercising in the code above.
If you don't ignore the returned value, as in:
T t = f();
there is conceptually a second copy from the returned value to the t
local variable. That second copy is being elided in all of your cases.
For the first copy, whether the object being returned is const
or not does not matter, the compiler determines what to do based on the arguments to the [conceptual copy/move] constructor, not whether the object being constructed will be const
or not. This is the same as:
// a is convertible to T somehow
const T ct(a);
T t(a);
Whether the destination object is const or not does not matter, the compiler needs to find the best constructor based on the arguments, not the destination.
Now if we take that back to your exercise, to make sure that the copy constructor is not called, you need to modify the argument to the return
statement:
A force_copy(const A&l,const A&r){ // A need not be `const`
if(l.m==0){return r;}
if(r.m==0){return l;}
const A sum;
return sum;
}
That should trigger copy construction, but then again it is simple enough that the compiler may elide the copy altogether if it finds it fit.