What is copy/move constructor choosing rule in C++? When does move-to-copy fallback happen?
c++, c++11, copy-constructor, move-constructor
Solution
There is no "fallback". It is called overload resolution. If there are more than one possible candidate in overload resolution then the best match is chosen, according to a complicated set of rules which you can find by reading the C++ standard or a draft of it.
Here is an example without constructors.
class X { };
void func(X &&) { cout << "move\n"; } // 1
void func(X const &) { cout << "copy\n"; } // 2
int main()
{
func( X{} );
}
- As-is: prints "move"
- Comment out "1": prints "copy"
- Comment out "2": prints "move"
- Comment out "1" and "2": fails to compile
In overload resolution, binding rvalue to rvalue has higher preference than lvalue to rvalue.
Here is a very similar example:
void func(int) { cout << "int\n"; } // 1
void func(long) { cout << "long\n"; } // 2
int main()
{
func(1);
}
- As-is: prints "int"
- Comment out "1": prints "long"
- Comment out "2": prints "int"
- Comment out "1" and "2": fails to compile
In overload resolution, an exact match is preferred to a conversion.
In your three examples on this thread we have:
1: Two candidate functions; rvalue prefers rvalue (as in my first example)
A(const A&);
A(A&&); // chosen
2: Two candidate functions; rvalue prefers rvalue (as in my first example)
A(const A&);
A(A&&); // chosen
3: One candidate function; no contest
A(const A&); // implicitly declared, chosen
As explained earlier, there is no implicit declaration of A(A&&) in case 3 because you have a destructor.
For overload resolution it does not matter whether the function body exists or not, it is whether the function is declared (either explicitly or implicitly).
Problem
The first example: ``` #include <iostream> #include <memory> using namespace std; struct A { unique_ptr<int> ref; A(const A&) = delete; A(A&&) = default; A(const int i) : ref(new int(i)) { } ~A() = default; }; int main() { A a[2] = { 0, 1 }; return 0; } ``` It works perfectly. So here the MOVE constructor is used. Let's remove the move constructor and add a copy one: ``` #include <iostream> #include <memory> using namespace std; struct A { unique_ptr<int> ref; A(const A&a) : ref( a.ref.get() ? new int(*a.ref) : nullptr ) { } A(A&&) = delete; A(const int i) : ref(new int(i)) { } ~A() = default; }; int main() { A a[2] = { 0, 1 }; return 0; } ``` Now the compilation falls with the error "use of deleted function ‘A::A(A&&)’" So the MOVE constructor is REQUIRED and there is no fall back to a COPY constructor. Now let's remove both copy- and move-constructors: ``` #include <iostream> #include <memory> using namespace std; struct A { unique_ptr<int> ref; A(const int i) : ref(new int(i)) { } ~A() = default; }; int main() { A a[2] = { 0, 1 }; return 0; } ``` And it falls with "use of deleted function ‘A::A(const A&)’" compilation error. Now it REQUIRES a COPY constructor! So there was a fallback (?) from the move constructor to the copy constructor. Why? Does anyone have any idea how does it conform to the C++ standard and what actually is the rule of choosing among copy/move constructors?