想象一下,我有一个类A
,移动成本低,复制成本高。它可能看起来像
class A {
public:
[...]
private:
HeavyClass m;
};
对于这个课程,我想要一个静态验证该课程 (1)移动可构造和 (2)不仅仅使用复制构造函数进行移动构造, 与是否明确声明移动构造函数无关。
这可能吗?
至于为什么我想这样做,请考虑以下示例:首先,该类自动生成移动构造函数,并根据需要运行。然后,有人更改了类并添加了析构函数,这导致不会隐式生成移动构造函数,而是使用复制构造函数。
因此,static_assert
是理想的,但似乎is_move_constructible
或is_trivially_move_constructible
都不适用于此。
另外,我知道所有这些类都可以A(A&&) = default;
,但是static_assert
的解决方案会更清晰,并允许在类定义之外进行检查(在其他类中为fi)依赖这个课程的项目。)
修改:
我不想禁止复制构造,我只想确保移动构造函数不使用它...
答案 0 :(得分:4)
如果您可以更改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, "");
答案 1 :(得分:0)
声明析构函数时,复制构造函数和复制赋值运算符的生成为deprecated。不需要静态断言或模板,这是现代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
传递它,那么您的编译器就不会抱怨。
答案 2 :(得分:0)
这是一个完全通用的维护问题。理想情况下,我们希望找到一个通用的解决方案。 手动添加断言是一个糟糕的解决方案。它容易出错,容易忘记,耗时并且使代码的可读性降低。
一个通用解决方案是使用静态分析器为所有类强制执行Rule of Zero。
Link to SO question about this。但是,如果编译器支持它,则添加编译器选项as described in my other answer是更好的解决方案。