禁用复制和移动语义的最简洁方法

Most concise way to disable copy and move semantics

以下确实有效但非常乏味:

T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

我正在尝试发现最简洁的方式。以下是否有效?

T& operator=(T) = delete;

更新

请注意,我选择 T& operator=(T) 而不是 T& operator=(const T&)T& operator=(T&&),因为它可以同时满足这两个目的。

根据这张图表(Howard Hinnant 绘制):

简洁的方法是=delete移动赋值运算符(或移动构造函数,但它会导致注释中提到的问题)。

不过,在我看来,最可读的方法是=delete 同时复制构造函数和复制赋值运算符。

您可以编写一个简单的 struct 并从中继承:

struct crippled
{
    crippled() = default;

    crippled(const crippled&) = delete;
    crippled(crippled&&) = delete;

    crippled& operator=(const crippled&) = delete;
    crippled& operator=(crippled&&) = delete;
};

用法:

struct my_class : crippled
{

};

int main()
{
    my_class a;
    auto b = a; // fails to compile
}

我更喜欢从 boost::noncopyable 继承,从而立即明确意图并将细节委托给值得信赖的图书馆。

#include <boost/core/noncopyable.hpp>

class X: private boost::noncopyable
{
};

它涉及添加一个依赖项,但如果您对此表示满意,那么它可以说是一种非常简洁和富有表现力的实现方式。

请不要试图寻找“最简洁的方式”来编写一段代码。

如果没有一种明显的表达方式也非常简洁 - 不要试图 language-lawyer 少写几个字符。为什么?想一想阅读您的代码的人:如果您需要查阅标准以了解您的代码执行您希望它执行的操作 - 那么您的代码读者也会这样做。除了他们不知道你想要达到什么目的;所以他们不会参考标准;所以他们只会对您的代码的作用感到困惑。或者 - 有些人会得到它,有些人不会。*

在你的情况下,如果你删除了这些删除的一个子集,或者使用了其他一些“聪明”的技巧——作为一个阅读你代码的人,我很可能不会理解,没有注意到你实际上在尝试删除所有复制和移动语义。我会感到困惑,以为你在尝试做其他事情。事实上,如果我是你,我什至会考虑添加评论说:

/* Disabling copy and move semantics because XYZ */
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

这更“乏味”,但会让您的 intention/motivation 对您未来的读者来说绝对清楚。

还有一个问题是“XYZ”原因究竟是什么。有些人会争辩说没有充分的理由删除移动成员,这样做通常是个坏主意。 C++ 名人 Howard Hinnant 对此事有

* - 我阐明的原则的变体 .

我相信在这种情况下,宏实际上更具可读性:

#define NOT_COPYABLE( TypeName ) \
TypeName ( TypeName const& ) = delete; \
TypeName & operator = ( TypeName const& ) = delete;

#define NOT_MOVEABLE( TypeName ) \
TypeName ( TypeName && ) = delete; \
TypeName & operator = ( TypeName && ) = delete;

suggested not inventing the wheel, and using boost::noncopyable as a mix-in 基地 class。这确实有效,但缺点是令人困惑! ...不可复制并不意味着 class 不可移动。一定要记住这个boost的命名的历史背景class;大多数人肯定不会,我也不会。

好吧,你也可以这样做,但是重命名这个 class:

#include <boost/core/noncopyable.hpp>

namespace mixins {
using noncopyable_and_nonmovable = boost::noncopyable;
} // namespace mixins 

class X: private mixins::noncopyable_and_nonmovable {
  // etc. etc.
};

现在你所做的事情已经很清楚了:-)

此外,您可能需要考虑使用此定义和可能的其他定义(例如,仅不可复制、仅不可移动等)创建一个小的混合命名空间到某些实用程序头文件中。这将使该解决方案使用起来更加简洁。


我仍然会考虑解释 原因 决定禁止复制和移动到某处;看看我的 .