什么是复制/移动构造函数在C ++中选择规则?什么时候移动到复制发生? [英] What is copy/move constructor choosing rule in C++? When does move-to-copy fallback happen?
问题描述
第一个示例:
#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构造函数。
让我们删除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 ;&)= delete;
A(const int i):ref(new int(i)){}
〜A()= default;
};
int main()
{
A a [2] = {0,1};
return 0;
}
现在编译出现错误 use of deleted function' A :: A(A&&)'
所以MOVE构造函数是必需的,并且没有回到COPY构造函数。
现在让我们移除复制和移动构造函数:
#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;
}
A(const A&)'编译错误。现在它需要一个COPY构造函数!
所以有一个从move构造函数到复制构造函数的回退(?)。
为什么?有没有人知道它是如何符合C ++标准和什么是复制/移动构造函数之间选择的规则?
p>没有后备。它被称为重载分辨率。如果在重载解析中有多个可能的候选项,则根据一组复杂的规则选择最佳匹配,您可以通过阅读C ++标准或其草稿来找到它们。
这里是没有构造函数的示例。
class X {};
void func(X&&){cout<< move\\\
; } // 1
void func(X const&){cout< copy\\\
; } // 2
int main()
{
func(X {});
}
- 原样:列印「move」
- 注释掉1:printscopy
- 注释掉2 li>注释掉1和2:无法编译
在重载解析中,
$ b
$ b
void func(int){cout< int\\\
; } // 1
void func(long){cout< long\\\
; } // 2
int main()
{
func(1);
}
- 原样:printsint
- 注释掉1:printslong
- li>注释掉1和2:无法编译
在重载解析中, 。
在这个线程的三个例子中,我们有:
1:两个候选函数; rvalue偏好rvalue(如我的第一个例子)
A(const A&
A(A&&); // selected
2:两个候选函数; rvalue偏好rvalue(如我的第一个例子)
A(const A&
A(A&&); // selected
3:一个候选函数;没有比赛
A(const A&); //隐式声明,选择
作为前面解释过, (A&&)在case 3,因为你有一个析构函数。
对于重载分辨率无论函数体是否存在, (显式或隐式)。
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?
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).
这篇关于什么是复制/移动构造函数在C ++中选择规则?什么时候移动到复制发生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!