在C ++ 11标准中对隐式和显式删除的移动构造函数的不同处理背后的 基本原理 是什么,关于隐式生成移动构造函数包含/继承类?
C ++ 14 / C ++ 17会改变什么吗? (C ++ 14中的DR1402除外)
注意:我理解发生了什么,我知道这是根据C ++ 11标准的规则,我对这些规则的理由感兴趣,这意味着这种行为(请确保不要简单地重申它是这样的,因为标准是这样说的。)
假设一个类ExplicitDelete
具有明确删除的移动ctor和明确默认的复制ctor。即使兼容的副本ctor可用,此类也不是move constructible
,因为重载决策选择了移动构造函数,并且由于删除而在编译时失败。
假设一个类ImplicitDelete
包含或继承自ExplicitDelete
,并且不执行任何其他操作。由于C++11 move ctor rules,此类将其移动ctor隐式声明为已删除。但是,此类仍将通过其副本ctor move constructible
。 (这最后的陈述是否与DR1402的解决方案有关?)
然后,包含/继承自Implicit
的类ImplicitDelete
将生成一个非常精细的隐式移动构造函数,该函数会调用ImplicitDelete
的复制文件。
那么允许Implicit
能够隐式移动而ImplicitDelete
无法隐式移动的原因是什么呢?
在实践中,如果Implicit
和ImplicitDelete
有一些重型可移动成员(想想vector<string>
),我认为没有理由Implicit
应该远远优于ImplicitDelete
{1}}移动表现。 ImplicitDelete
仍然可以将ExplicitDelete
从其隐式移动ctor中复制 - 就像Implicit
与ImplicitDelete
一样。
对我来说,这种行为似乎不一致。如果发生以下两种情况之一,我会发现它更加一致:
编译器同时处理隐式和显式删除的移动ctors:
ImplicitDelete
变得不是move-constructible
,就像ExplicitDelete
ImplicitDelete
已删除的移动ctor会导致Implicit
中删除的隐式移动ctor(与ExplicitDelete
执行ImplicitDelete
的方式相同<) / LI>
Implicit
变为move-constructible
std::move
行的编译完全失败
或者,编译器回退到ExplicitDelete
复制ctor :
ExplicitDelete
中都会调用move
的复制构造函数,就像ImplicitDelete
ImplicitDelete
得到了正确的隐含动作ctor Implicit
在此方案中未更改)Explicit
成员始终被移动。以下是完整的工作示例:
#include <utility>
#include <iostream>
using namespace std;
struct Explicit {
// prints whether the containing class's move or copy constructor was called
// in practice this would be the expensive vector<string>
string owner;
Explicit(string owner) : owner(owner) {};
Explicit(const Explicit& o) { cout << o.owner << " is actually copying\n"; }
Explicit(Explicit&& o) noexcept { cout << o.owner << " is moving\n"; }
};
struct ExplicitDelete {
ExplicitDelete() = default;
ExplicitDelete(const ExplicitDelete&) = default;
ExplicitDelete(ExplicitDelete&&) noexcept = delete;
};
struct ImplicitDelete : ExplicitDelete {
Explicit exp{"ImplicitDelete"};
};
struct Implicit : ImplicitDelete {
Explicit exp{"Implicit"};
};
int main() {
ImplicitDelete id1;
ImplicitDelete id2(move(id1)); // expect copy call
Implicit i1;
Implicit i2(move(i1)); // expect 1x ImplicitDelete's copy and 1x Implicit's move
return 0;
}
答案 0 :(得分:5)
那么允许Implicit能够隐式移动而ImplicitDelete无法隐式移动的原因是什么呢?
理由是:您描述的案例没有意义。
看,所有这一切都是因为ExplicitDelete
而开始的。根据您的定义,此类具有显式删除的移动构造函数,但是具有默认的复制构造函数。
有固定类型,既没有复制也没有移动。有移动类型。还有可复制的类型。
但是可以复制但有显式删除移动构造函数的类型?我会说这样的课程是矛盾的。
以下是我看到的三个事实:
明确删除移动构造函数应该意味着您无法移动它。
明确地默认复制构造函数应该意味着你可以复制它(当然是为了这个对话的目的。我知道你仍然可以做一些事情,而不是删除显式默认值。)
如果可以复制类型,则可以移动它。这就是为什么存在关于隐式删除的移动构造函数不参与重载解析的规则的原因。因此,移动是复制的适当子集。
在这种情况下,C ++的行为是不一致的,因为你的代码是矛盾的。您希望您的类型可以复制但不可移动; C ++不允许这样做,所以它表现得很奇怪。
看看当你消除矛盾时会发生什么。如果您在ExplicitDelete
中明确删除了复制构造函数,那么一切都有意义。 ImplicitDelete
的复制/移动构造函数被隐式删除,因此它是不可移动的。 Implicit
的复制/移动构造函数被隐式删除,因此它也是不可移动的。
如果编写矛盾的代码,C ++将不会以完全合法的方式运行。