与复制构造函数不同的移动构造函数的静态断言



想象一下,我有一个类A,它的移动成本很低,复制成本很高。它可能看起来像

class A {
public: 
[...]
private:
HeavyClass m;
};

对于这个类,我想要一个静态验证(1) 是可移动的,并且(2) 不简单地使用复制构造函数进行移动构造,独立于是否显式声明了move构造函数。

这可能吗?

至于我为什么要这样做,请考虑以下示例:首先,该类自动生成move构造函数,并根据需要进行操作。然后,有人更改了类并添加了一个析构函数,这导致移动构造函数不会隐式生成,而是使用复制构造函数。

因此,static_assert将是理想的,但似乎is_move_constructibleis_trivially_move_constructible在这里都没有帮助。

此外,我知道所有这样的类都可以使用A(A&&) = default;,但使用static_assert的解决方案会更干净,并允许在类定义之外进行检查(例如,在其他依赖此类的项目中)。

编辑
我不想禁止复制构造,我只想确保move构造函数没有使用它…

如果可以将A更改为具有间接寻址,则可以执行以下操作:

template <bool>
struct MoveOnly {
MoveOnly() = default;
~MoveOnly() = default;
MoveOnly(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly& operator=(MoveOnly&&) = default;
};
template <> struct MoveOnly<false> {};
template <bool check = false>
class A_Impl : MoveOnly<check> {
public: 
// ... as ~A_Impl() {}
// ...
private:
HeavyClass m;
};
using A = A_Impl<false>; // Normal case
// The check
static_assert(std::is_move_constructible<A_Impl<true>>::value, "");

演示

当声明析构函数时,不赞成生成复制构造函数和复制赋值运算符。不需要静态断言或模板,这是现代C++的一部分。

解决方案只是启用折旧警告

如果你还没有这样做,也可以把警告变成错误。

这样,您就不必记住在所有地方添加静态断言。此外,只要您不复制或移动此类对象的任何实例,您仍然可以添加析构函数、拥有不可移动的成员和从不可移动对象继承。当限制不适用时,没有意义。

使用此设置,您可以尝试将此代码添加到类中(典型的回归)

virtual ~A() = default;

如果现在尝试移动复制A的实例,则编译将失败。

来自clang-3.9 -Werror -Wdeprecated的错误消息示例

main.cpp:13:13: error: definition of implicit copy assignment
operator for 'A' is deprecated because it has a user-declared 
destructor [-Werror,-Wdeprecated]
virtual ~A() = default;
^
main.cpp:21:7: note: implicit copy assignment operator for
'A' first required here
b = std::move(a);
^
1 error generated.

如果您创建了A的实例,并简单地通过const reference传递它,那么编译器不会抱怨

这是一个完全通用的维护问题。理想情况下,我们希望找到一个通用的解决方案手动添加断言是一个糟糕的解决方案。它容易出错、容易忘记、耗时,并且会降低代码的可读性。

一个通用的解决方案是使用静态分析器对所有类强制执行零规则。

链接到SO关于此的问题。然而,如果你的编译器支持的话,添加我的另一个答案中描述的编译器选项是一个更好的解决方案

最新更新