C++ 即使未使用Move构造函数,也需要它。为什么?
为什么?!为什么C++需要这个类即使它没有被使用也是可移动的! 例如:C++ 即使未使用Move构造函数,也需要它。为什么?,c++,c++11,std,c++14,C++,C++11,Std,C++14,为什么?!为什么C++需要这个类即使它没有被使用也是可移动的! 例如: #include <iostream> using namespace std; struct A { const int idx; // It could not be compileld if I comment out the next line and uncomment // the line after the next but the moving constructo
#include <iostream>
using namespace std;
struct A {
const int idx;
// It could not be compileld if I comment out the next line and uncomment
// the line after the next but the moving constructor is NOT called anyway!
A(A&& a) : idx(a.idx) { cout<<"Moving constructor with idx="<<idx<<endl; }
// A(A&& a) = delete;
A(const int i) : idx(i) { cout<<"Constructor with idx="<<i<<endl; }
~A() { cout<<"Destructor with idx="<<idx<<endl; }
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
// The array of this class can not be initialized!
class B {
unique_ptr<int> ref;
public:
B(int* ptr) : ref(ptr)
{ }
}
// The next class can not be even compiled!
class C {
B arrayOfB[2] = { NULL, NULL };
}
#包括
使用名称空间std;
结构A{
常数int idx;
//如果我注释掉下一行并取消注释,它将无法编译
//下一行之后的行,但无论如何都不会调用移动构造函数!
A(A&&A):idx(A.idx){cout
从概念上讲,这将创建两个临时A
对象,A(0)
和A(1)
,并移动或复制它们以初始化数组A
;因此需要移动或复制构造函数
作为一种优化,允许省略移动或复制,这就是为什么您的程序似乎没有使用移动构造函数。但必须仍然有一个合适的构造函数,即使它的使用被省略
A a[2] = { 0, 1 };
这是聚合初始化。标准§8.5.1[dcl.init.aggr]/p2规定
当聚合由初始值设定项列表初始化时,如8.5.4所述,初始值设定项列表的元素被视为聚合成员的初始值设定项,以递增的下标或成员顺序。每个成员都从相应的初始值设定项子句复制初始化
§8.5[dcl.init]/p16反过来描述了类类型对象的复制初始化语义:
- [……]
- 如果目标类型是(可能是cv限定的)类类型:
- 如果初始化是直接初始化,或者如果是复制初始化,则源的cv版本不合格
类型与的类相同,或是的类的派生类
目的地,考虑构造函数。适用的构造函数
列举了(13.3.1.3),并通过重载选择了最佳的
解析(13.3)。调用如此选择的构造函数进行初始化
对象,其初始值设定项表达式或表达式列表为
参数。如果没有应用构造函数,或者重载解析为
不明确,初始化的格式不正确
- 否则(即,对于剩余的复制初始化情况),可以从源转换的用户定义的转换序列
输入到目标类型或(使用转换函数时)
如13.3.1.4所述,对其派生类进行枚举,
通过过载分辨率(13.3)选择最佳
转换无法完成或不明确,初始化无效
格式错误。使用初始值设定项调用所选函数
表达式作为其参数;如果函数是构造函数,则调用
初始化的cv非限定版本的临时
目的地类型。临时值是prvalue。调用的结果
(对于构造函数来说是临时的)然后用于
根据上面的规则,直接初始化
复制初始化的目标。在某些情况下
允许实现以消除此过程中固有的复制
通过直接构造中间结果直接初始化
进入正在初始化的对象;参见12.2、12.8
由于0
和1
是int
s,而不是A
s,这里的复制初始化属于第二个子句。编译器在A::A(int)中找到从int
到A
的用户定义转换
构造函数,调用它来构造类型为a
的临时构造函数,然后使用该临时构造函数执行直接初始化。这反过来意味着编译器需要对类型为a
的临时构造函数执行重载解析,该构造函数选择已删除的移动构造函数,从而使程序出错-形成。C++不要求类在不移动的情况下可以移动。你得出了错误的结论。不,不,不,不……它需要一个移动的构造函数,但它不使用它。所以类不移动,但构造函数是必需的。我的结论怎么了?不,不,你完全错了。好的,让我们一步一步来做。1)是cl吗ass moved?2)是否需要移动构造函数?我如何知道何时未调用构造函数?@user3544995您不必在意。您的复制/移动构造函数不应有任何其他副作用,但正如您所演示的,您可以引入副作用(如使用cout
打印)只是看看它是否被调用。@user3544995:通常情况下,您不会在意-无论移动是否被省略,程序的行为都是相同的,并且省略将是一个纯粹的优化。您只会注意到以下情况的差异(如此处所示)它做的不是简单地移动对象,这是一件奇怪的事情。@user3544995,因为复制/移动省略是可选的优化。事实上,编译器可以省略任何内容,只要它不影响程序的执行(这称为“仿佛规则”)。明确提到复制/移动构造函数的唯一原因是,如果复制/移动构造函数有副作用(这会影响程序的执行),它甚至会发生。@user3544995:“如果有文件证明移动构造函数没有被调用,为什么要求它出现?”-不是,有文件证明编译器可以选择是否调用它。它必须存在,因为否则代码的有效性将取决于编译器的优化选择。哦,Thanx。但是它仍然看起来很奇怪-需要一些声明为未使用的。顺便问一下,如果我需要一个大的“否”,你知道有什么好的模式可以使用吗要在数组中初始化n-copyable类?我有一个向量为unique_ptr的类。如何在数组中初始化它?这个类非常大,我真的不想为它编写复制/移动构造函数。@user3544995一个向量为unique_ptr
s应该是可移动的。请看我的第二个例子
A a[2] = { 0, 1 };