Reputation: 101
For what reason move constructor call is not elided in following code?
struct Foo {
Foo() = default;
Foo(const Foo&) { cout << "Foo(const Foo&)" << endl; }
template<class T> Foo(T&&) { cout << "Foo<T>(T&&)" << endl; }
};
auto f = Foo{};
Output: Foo<T>(T&&)
Checked on clang 3.3, g++ 4.9.
Adding defaulted or user-defined move constructor results in no output at all. Why the call to move constructor template is not elided by the compiler? And why the call to non-template one gets elided even if it's user-defined (i.e. how does the compiler know that it has no side-effects and can be safely elided)?
Upvotes: 1
Views: 317
Reputation: 101
The reason is because copy and move constructors are defined to be non-template (12.8/2, 12.8/3), copy/move elision rules do not apply for this constructor template (12.8/31).
The addition of defaulted or user defined move constructor (in this case) results in different output (and move elision) because user-defined copy constructor prevents compiler from implicitly generating a default one, thus the answer was wrong in first place:
12.8/7 If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4).
Upvotes: 0
Reputation: 52314
To be a move constructor and thus candidate for elision, the constructor must not be a template instantiation (similarly, the fact that a template can generate a constructor with the same signature will not prevent the defaulted version to be generated).
In your example, the presence of the explicit copy constructor prevent the implicit generation of a default move constructor, so the constructor template is instantiated (and is not a move constructor even if it share the signature).
If you add an explicit (default or not) move constructor, it will be used (and can -- but not must -- be elided).
Upvotes: 4