Copy elision for can only occur in a few specific situations, the most common of which is the copying of a temporary (the others are returning locals, and throwing/catching exceptions). There is no temporary being produced by your code, so no copy is elided.
The copy constructor is being called because foo does not have a move constructor (move constructors are not implicitly generated for classes with explicit copy constructors), and so std::move(a) matches the foo(const foo &rhs) constructor (which is used to construct the function argument).
A copy of an lvalue can be elided in the following situations (although there is no way to force a compiler to perform the elision):
foo fn() { foo localAutomaticVariable; return localAutomaticVariable; //Copy to construct return value may be elided } int main() { try { foo localVariable; throw localVariable; //The copy to construct the exception may be elided } catch(...) {} }
If you want to avoid copies when passing function arguments, you can use a move constructor which pilfers the resources of the objects given to it:
class bar { public: bar() {cout<<"ctor"<<endl;}; bar(const bar &rhs) {cout<<"copy ctor"<<endl;} bar(bar &&rhs) {cout<<"move ctor"<<endl;} }; void fn(bar a) { } //Prints: //"ctor" //"move ctor" int main() { bar b; f(std::move(b)); }
Also, whenever copy elision is allowed but does not occur, the move constructor will be used if it is available.
gtakes its parameter by value so the compiler has to ensure that the object passed is distinct from any object accessible from the calling scope. If the object being passed is an lvalue there is no temporary to eliminate and a copy cannot be elided.std::moveactually does.