不同于复制构造函数的移动构造函数的静态断言
Static assertion for a move constructor different from the copy constructor
想象一下,我有一个 class A
,移动成本低,复制成本高。它可能看起来像
class A {
public:
[...]
private:
HeavyClass m;
};
对于这个 class,我想要一个静态验证 class
(1) 是移动可构造的并且
(2) 不简单的使用拷贝构造函数进行move构造,
与移动构造函数是否显式声明无关。
这可能吗?
至于为什么我会喜欢这个,请考虑以下示例:首先,class 自动生成移动构造函数并按预期运行。然后,有人更改class并添加了一个析构函数,这导致移动构造函数不被隐式生成,而是使用复制构造函数。
因此,static_assert
将是理想的选择,但似乎 is_move_constructible
或 is_trivially_move_constructible
的 none 在这里很有帮助。
此外,我知道可以为所有此类 class 设置 A(A&&) = default;
,但带有 static_assert
的解决方案会更干净,并允许在外部进行检查class 定义(f.i。在依赖此 class 的其他项目中)。
编辑:
我不想禁止复制构造,我只是想确保移动构造函数没有使用它...
如果您可以将 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, "");
复制构造函数和复制赋值运算符的生成是deprecated声明析构函数时。不需要静态断言或模板,这是现代 C++ 的一部分。
解决方案就是启用弃用警告
如果您还没有将警告转化为错误,也将其转化为错误。
这样您就不必记得在整个过程中添加静态断言。此外,您仍然可以添加析构函数,拥有 non-moveable 成员并继承 non-moveables ,只要您不复制或移动此类对象的任何实例 .不适用的限制没有意义。
有了这个设置你可以尝试把这段代码添加到你class(一个典型的回归)
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
传递它,那么你的编译器就不会抱怨了。
这是一个完全通用的维护问题。理想情况下,我们希望为此找到一个通用的解决方案。 手动 添加断言是一个糟糕的解决方案。它容易出错、容易忘记、耗时并且降低代码的可读性。
一个通用的解决方案是使用静态分析器为所有 类.
强制执行 Rule of Zero
如果您的编译器支持,Link to SO question about this. However, adding compiler options 是更好的解决方案。
想象一下,我有一个 class A
,移动成本低,复制成本高。它可能看起来像
class A {
public:
[...]
private:
HeavyClass m;
};
对于这个 class,我想要一个静态验证 class (1) 是移动可构造的并且 (2) 不简单的使用拷贝构造函数进行move构造, 与移动构造函数是否显式声明无关。
这可能吗?
至于为什么我会喜欢这个,请考虑以下示例:首先,class 自动生成移动构造函数并按预期运行。然后,有人更改class并添加了一个析构函数,这导致移动构造函数不被隐式生成,而是使用复制构造函数。
因此,static_assert
将是理想的选择,但似乎 is_move_constructible
或 is_trivially_move_constructible
的 none 在这里很有帮助。
此外,我知道可以为所有此类 class 设置 A(A&&) = default;
,但带有 static_assert
的解决方案会更干净,并允许在外部进行检查class 定义(f.i。在依赖此 class 的其他项目中)。
编辑:
我不想禁止复制构造,我只是想确保移动构造函数没有使用它...
如果您可以将 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, "");
复制构造函数和复制赋值运算符的生成是deprecated声明析构函数时。不需要静态断言或模板,这是现代 C++ 的一部分。
解决方案就是启用弃用警告
如果您还没有将警告转化为错误,也将其转化为错误。
这样您就不必记得在整个过程中添加静态断言。此外,您仍然可以添加析构函数,拥有 non-moveable 成员并继承 non-moveables ,只要您不复制或移动此类对象的任何实例 .不适用的限制没有意义。
有了这个设置你可以尝试把这段代码添加到你class(一个典型的回归)
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
传递它,那么你的编译器就不会抱怨了。
这是一个完全通用的维护问题。理想情况下,我们希望为此找到一个通用的解决方案。 手动 添加断言是一个糟糕的解决方案。它容易出错、容易忘记、耗时并且降低代码的可读性。
一个通用的解决方案是使用静态分析器为所有 类.
强制执行 Rule of Zero 如果您的编译器支持,Link to SO question about this. However, adding compiler options