第一个例子:
#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;
}
完美无缺。所以这里使用了MOVE构造函数。
让我们删除移动构造函数并添加一个副本:
#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;
}
现在编辑属于错误&#34; 使用已删除的功能'A :: A(A&amp;&amp;)'&#34;
所以MOVE构造函数是必需的,没有回退到COPY构造函数。
现在让我们删除copy-move和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;
}
并且&#34; 使用已删除的功能'A :: A(const A&amp;)'&#34;编译错误。现在它需要一个COPY构造函数!
因此从移动构造函数到复制构造函数有一个回退(?)。
为什么?有没有人知道它如何符合C ++标准,以及在复制/移动构造函数中选择的实际规则是什么?
答案 0 :(得分:9)
在检查它是否为delete
d之前,选择它将要使用的函数。在复制构造函数可用且移动构造函数为delete
d的情况下,移动构造函数仍然是两者中的最佳选择。然后它看到它是delete
d并且给你一个错误。
如果你有相同的例子,但实际上删除了移动构造函数,而不是使它成为delete
d,你会看到它编译得很好并且回退使用复制构造函数:
#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(const int i) : ref(new int(i)) { }
~A() = default;
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
这个类根本没有为它声明的移动构造函数(甚至没有隐式),因此无法选择它。
答案 1 :(得分:8)
没有&#34;后退&#34;。它被称为重载决策。如果在重载决策中有多个可能的候选者,则根据一组复杂的规则选择最佳匹配,您可以通过阅读C ++标准或其草稿来找到它。
这是一个没有构造函数的例子。
class X { };
void func(X &&) { cout << "move\n"; } // 1
void func(X const &) { cout << "copy\n"; } // 2
int main()
{
func( X{} );
}
在重载决策中,将rvalue绑定到rvalue的优先级高于左值的左值。
这是一个非常相似的例子:
void func(int) { cout << "int\n"; } // 1
void func(long) { cout << "long\n"; } // 2
int main()
{
func(1);
}
在重载分辨率中,完全匹配是转换的首选。
在这个主题的三个例子中,我们有:
1:两个候选功能; rvalue更喜欢rvalue(如我的第一个例子)
A(const A&);
A(A&&); // chosen
2:两个候选功能; rvalue更喜欢rvalue(如我的第一个例子)
A(const A&);
A(A&&); // chosen
3:一个候选功能;没有比赛
A(const A&); // implicitly declared, chosen
作为explained earlier,案例3中没有隐含的A(A&amp;&amp;)声明,因为你有一个析构函数。
对于重载决策,函数体是否存在并不重要,它是否声明了函数(显式或隐式)。