即使不使用移动构造函数,也需要它.为什么
Move constructor is required even if it is not used. Why?
为什么?!为什么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;
}
输出是(不调用移动构造函数!
idx=0
的构造函数 idx=1
的构造函数 idx=1
的析构函数 idx=0 的析构函数
如果删除移动构造函数(">使用已删除的函数"A::A(A&&("(,则无法编译代码。但是,如果未删除构造函数,则不会使用它!多么愚蠢的限制?注意:我为什么需要它?当我尝试初始化包含unique_ptr字段的对象数组时,实际含义就出现了。例如:
// 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 };
}
如果您尝试使用unique_ptr的向量,情况会变得更糟。
好。非常感谢大家。所有这些复制/移动构造函数和数组初始化都有很大的混淆。实际上,问题是关于编译器需要复制结构的情况,可以使用移动结构并且不使用它们。因此,当我获得普通键盘时,我将稍后创建一个新问题。我将在此处提供链接。
附言我创建了更具体和清晰的问题 - 欢迎讨论它!
A a[2] = { 0, 1 };
从概念上讲,这会创建两个临时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 非限定版本的临时版本 目标类型。临时是原则。调用的结果 (这是构造函数情况的临时(然后用于 根据上述规则,直接初始化对象 复制初始化的目标。在某些情况下,一个 允许实施以消除此固有的复制 通过直接构造中间结果进行直接初始化 到正在初始化的对象中;参见 12.2、12.8。
由于 0
和 1
是 int
s,而不是 A
s,因此此处的副本初始化属于第二个子句。编译器在A::A(int)
构造函数中找到从int
到A
的用户定义转换,调用它来构造类型A
的临时,然后使用该临时函数执行直接初始化。反过来,这意味着编译器需要为采用 A
类型的临时构造函数执行重载解析,这会选择已删除的移动构造函数,从而使程序格式不正确。
- 为什么C++构造函数在继承中需要默认参数?
- 为什么构造函数 Message(const T&data) 与 Message(T&& data) 冲突,当 T = int&时?
- 为什么构造函数的虚拟函数调用有时有效,但其他调用却无效
- 在C++中,当重新分配对象时,为什么构造函数在析构函数之前触发?
- 重载运算符 new(),为什么构造函数被调用两次?
- 为什么构造函数C++接受不正确的类型作为参数?
- 为什么构造函数的数量与Destructor调用的数量不匹配
- 为什么构造函数没有被多次调用
- 为什么构造函数没有得到对数组中第二个对象的调用
- 为什么构造函数忽略重写的虚函数
- 为什么构造函数调用依赖于默认析构函数的存在?
- 为什么构造函数被调用两次
- 为什么构造函数在C++中被调用了两次
- 为什么构造函数优先于用户定义的运算符
- 为什么构造函数"return"这个指针?
- 为什么构造函数/析构函数在g++生成的程序集代码中是这样定义的
- 为什么构造函数不能在 c++ 中声明为静态?
- 为什么构造函数在某些情况下不起作用
- 为什么构造函数上的显式说明符不能防止向上转换?
- 为什么构造函数总是具有与类相同的名称,以及如何隐式调用它们