C++ 即使未使用Move构造函数,也需要它。为什么?

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

为什么?!为什么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 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 };