在做了一些研究之后,我看到C++11 has a defect的分配器要求类型可移动/可复制。我确定这是导致这个问题的原因,但我对删除和未声明的移动语义之间的行为感到困惑。
我有以下代码无法在MSVC12和Clang上编译:
#include <vector>
class Copyable
{
public:
Copyable() = default;
Copyable(Copyable const& other)
: m_int(other.m_int)
{}
Copyable& operator= (Copyable const& other)
{
m_int = other.m_int;
return *this;
}
Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;
private:
int m_int = 100;
};
int main()
{
std::vector<Copyable> objects;
objects.push_back(Copyable{});
}
无法在MSVC上编译:
xmemory0(600):错误C2280:'可复制::可复制(可复制&amp;&amp;)':尝试引用已删除的函数
关于Clang(live sample):
new_allocator.h:120:23:错误:调用'Copyable'的已删除构造函数
在这两种情况下,当我删除显式删除的移动构造/分配方法时,代码会编译。 AFAIK在声明复制赋值/构造方法时,编译器不会隐式声明相应的移动成员。所以它们仍应被有效删除,对吧?为什么在删除move construct / assign的显式删除时编译代码?
一般来说,这个C ++ 11缺陷有什么好的解决方法?我不希望我的物体可以移动(但它们是可复制的)。
答案 0 :(得分:14)
删除函数与不声明函数不同。
声明已删除的函数并参与重载解析,但如果您尝试调用它,则会产生错误。
如果未能声明移动构造函数,编译器将不会在创建复制构造函数时创建一个。右边上的重载分辨率将找到你的复制构造函数,这可能是你想要的。
您对foo(foo&&)=delete
的评价是&#34;如果有人试图移动构造此对象,则会生成错误&#34;。
我可以在这里说明不同之处:
void do_stuff( int x ) { std::cout << x << "\n"; }
void do_stuff( double ) = delete;
void do_stuff2( int x ) { std::cout << x << "\n"; }
//void do_stuff2( double ) = delete;
int main() {
do_stuff(3); // works
//do_stuff(3.14); // fails to compile
do_stuff2(3); // works
do_stuff2(3.14); // works, calls do_stuff2(int)
}
唯一有上述问题的部分会让人感到更加困惑,因为特殊的成员函数会自动创建或不基于一些神秘的规则。
答案 1 :(得分:10)
Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;
除非你是移动语义方面的专家(我的意思是,真的知识渊博),否则永远不要删除特殊移动成员。它不会做你想做的事。如果您查看其他人执行此操作的代码,请将其调出。解释必须非常可靠,而不是&#34;因为我不希望类型移动。&#34;
不要这样做。
执行所需操作的正确方法是简单地声明/定义复制成员。移动成员将被隐式禁止(不删除,但实际上不存在)。只需编写C ++ 98/03。
有关详细信息,请see this answer。