C++11 标准中隐式和显式删除 move 构造函数的不同处理背后的基本原理是什么,关于 move 构造函数的隐式生成包含/继承类?
C++14/C++17 有什么改变吗? (C++14 中的 DR1402 除外)
注意:我明白发生了什么,我明白这是根据 C++11 标准的规则,我对这些暗示这种行为的规则的基本原理感兴趣(请确保不要简单地重申它是这样的,因为标准是这样说的)。
假设一个类ExplicitDelete
带有一个显式删除的 move 构造函数和一个显式默认的复制构造函数。这个类不是 move constructible
即使有一个兼容的复制构造函数可用,因为重载决策选择了 move 构造函数并且由于它被删除而在编译时失败。
假设一个类ImplicitDelete
它包含或继承自 ExplicitDelete
什么都不做。由于 C++11 move ctor rules,此类将其 move 构造函数隐式声明为已删除.但是,这个类仍然是 move constructible
通过它的复制ctor。 (最后这句话是否与 DR1402 的决议有关?)
然后是一个类Implicit
包含/继承自 ImplicitDelete
将生成一个非常好的隐式 move 构造函数,调用 ImplicitDelete
的拷贝构造器。
那么允许 Implicit
背后的基本原理是什么?能够隐式 move 和ImplicitDelete
不能隐式 move ?
在实践中,如果Implicit
和 ImplicitDelete
有一些重型可 move 部件(想想vector<string>
),我认为没有理由Implicit
应该大大优于 ImplicitDelete
在 Action 表现上。 ImplicitDelete
仍然可以复制 ExplicitDelete
从它的隐式 move 构造函数——就像Implicit
用 ImplicitDelete
做.
对我来说,这种行为似乎不一致。如果发生这两件事中的任何一件,我会发现它更一致:
编译器对隐式和显式删除的 move 构造函数一视同仁:
-
ImplicitDelete
变得不move-constructible
, 就像ExplicitDelete
-
ImplicitDelete
已删除的 move 构造函数导致Implicit
中已删除的隐式 move 构造函数(与ExplicitDelete
对ImplicitDelete
的处理方式相同) -
Implicit
变得不move-constructible
- 编译
std::move
我的代码示例中的行完全失败
-
或者,编译器回退为
ExplicitDelete
复制 ctor also :-
ExplicitDelete
的复制构造函数在所有move
中被调用s,就像ImplicitDelete
-
ImplicitDelete
得到一个合适的隐式 move 构造函数 - (
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;
}
最佳答案
So what is the rationale behind allowing Implicit to be able to move implicitly and ImplicitDelete not to be able to move implicitly?
基本原理是这样的:您描述的情况没有意义。
看,这一切都是因为 ExplicitDelete
开始的。根据您的定义,此类有一个显式删除的 move 构造函数,但有一个默认的复制构造函数。
有固定类型,既没有复制也没有 move 。有仅 move 类型。还有可复制的类型。
但是可以复制但具有显式删除 move 构造函数的类型?我会说这样的类是矛盾的。
以下是我所看到的三个事实:
显式删除 move 构造函数应该意味着您不能 move 它。
显式默认复制构造函数应该意味着您可以复制它(当然是为了本次对话的目的。我知道您仍然可以做一些事情来删除显式默认值)。
如果一个类型可以被复制,它就可以被 move 。这就是为什么存在关于不参与重载决策的隐式删除 move 构造函数的规则。因此, move 是复制的真子集。
在这种情况下,C++ 的行为是不一致的,因为您的代码是矛盾的。您希望您的类型可复制但不可 move ; C++ 不允许这样做,因此它的行为很奇怪。
看看消除矛盾后会发生什么。如果您在 ExplicitDelete
中显式删除复制构造函数,一切都会再次变得有意义。 ImplicitDelete
的复制/move 构造函数被隐式删除,因此它是不可 move 的。并且 Implicit
的复制/move 构造函数被隐式删除,因此它也是不可 move 的。
如果您编写自相矛盾的代码,C++ 将不会以完全合法的方式运行。
关于c++ - 为什么隐式和显式删除的 move 构造函数会受到不同对待?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33988934/