在下面的代码中:
struct copy_only
{
copy_only() = default;
copy_only(const copy_only&) = default;
copy_only& operator=(const copy_only&) = default;
copy_only(copy_only&&) = delete;
copy_only& operator=(copy_only&&) = delete;
~copy_only() = default;
};
std::vector<copy_only> v;
copy_only c{};
v.push_back(c);
在 MSVC 上我们得到错误:
错误 C2280:'copy_only::copy_only(copy_only &&)':尝试引用已删除的函数
这来自于向量实现中push_back(const&)
调用的emplace_back
实现:
注意:编译类模板成员函数'void std::vector<copy_only,std::allocator<copy_only>>::push_back(const _Ty &)'
注意:请参阅正在编译的函数模板实例化“_Ty &std::vector<_Ty,std::allocator<_Ty>>::_Emplace_one_at_back<const _Ty&>(const _Ty &)”的引用
这可以用 gcc 和 clang 编译。这仅仅是 MSVC 编译器在最简单的向量示例上出现的严重 bug 吗?还是说存在一些标准问题,导致 gcc 和 clang 只是敷衍了事,阻止了这种用法?
TL;DR:这是一个写得很差的课程。
可复制类不应该主动抵制移动,即使它们不想有任何自定义移动行为。
相反,他们应该让移动回退到复制,如果你根本不写移动操作,这将自动发生,而不是明确地删除它们。
这不仅仅是不能很好地兼容
std::vector
。这会不断引发问题,因为没有人期望类会这样做。vector::push_back()
有一个前提条件“T
是Cpp17CopyInsertable到X
”,并且根据[container.alloc.reqmts-2.4] Cpp17CopyInsertable意味着Cpp17MoveInsertable:由于您的类不是Cpp17MoveInsertable,因此也不是Cpp17CopyInsertable,这违反了先决条件
push_back()
并导致未定义的行为。这里的问题是,显式删除的函数仍然是重载解析的候选函数,这实际上导致
CopyInsertable
需求失败。相反,你可以显式定义复制操作,并依赖编译器删除(即不生成)移动操作,这样就不再考虑移动操作,而是使用复制操作: