【问题标题】:Uncopyable class with automatic default and move constructors具有自动默认和移动构造函数的不可复制类
【发布时间】:2014-05-18 13:32:13
【问题描述】:

我想让一些类使用自动生成的构造函数,但不可复制(但仍可移动)。目前我正在这样做:

class A
{
public:
    A() = default;
    A(const A&) = delete;
    A(A&&) = default;
    A& operator=(const A&) = delete;
    A& operator=(A&&) = default;
}

我想知道是否真的有必要如此明确。如果我这样写会怎样:

class A
{
    A(const A&) = delete;
    A& operator=(const A&) = delete;
}

它仍然可以正常工作吗?其他情况下的最小默认值和删除集是什么 - 不可复制的不可移动类和带有虚拟析构函数的类?

是否有任何测试代码可以用来快速查看隐式创建的构造函数?

【问题讨论】:

  • 为什么要让类不可复制?是因为它有不可复制的成员吗?在这种情况下,该类无论如何都是不可复制的。

标签: c++ c++11 move-constructor default-copy-constructor


【解决方案1】:

这不起作用,因为不会自动为您创建默认构造函数。不会创建默认构造函数,因为您已经声明了一个复制构造函数。它被定义为已删除,但它仍然是用户声明的,因此没有隐式默认的默认构造函数。

隐式创建的构造函数的精简规则是:

  • 默认的移动构造函数和默认的移动赋值运算符是隐式创建的,除非您声明了任何其他 Big 5 特殊函数(并且除非被不可移动的成员或基阻止)
  • 默认的默认构造函数(多么好的名字!)是隐式创建的,除非您声明了任何构造函数(并且除非被不可默认创建的成员或基阻止)
  • 除非您声明了移动构造函数或移动赋值运算符(并且除非被不可复制的成员或基阻止),否则将创建默认的复制构造函数和默认的复制赋值运算符

【讨论】:

  • 所以,总结一下——为了使一个类不可复制但可移动,我默认默认构造函数、移动构造函数和移动赋值运算符,并且没有声明任何其他内容。谢谢!
  • 我实际测试过,复制构造函数在声明移动构造函数或移动赋值运算符时被隐式删除。 TDM-GCC 4.8.1 给出了很好的错误消息:“'constexpr A::A(const A&)' 被隐式声明为已删除,因为 'A' 声明了移动构造函数或移动赋值运算符”
  • @Xirdus 你是对的...错过了一些规则,再次编辑
  • @n.m.在类范围内使用BOOST_MOVABLE_BUT_NOT_COPYABLE 声明怎么样?
  • @hellfire769 我真的不想为这个项目引入 Boost。到目前为止,C++11 标准库拥有我需要的一切,而且很可能是我永远需要的。
【解决方案2】:

是否有任何测试代码可以用来快速查看哪些构造函数是 隐式创建?

是的。例如:

#include <type_traits>

class A
{
public:
    A() = default;
    A(A&&) = default;
    A& operator=(A&&) = default;
};

static_assert(std::is_nothrow_default_constructible<A>::value,
              "A must be noexcept default constructible");
static_assert(std::is_nothrow_destructible<A>::value,
              "A must be noexcept destructible");
static_assert(!std::is_copy_constructible<A>::value,
              "A must not be copy constructible");
static_assert(!std::is_copy_assignable<A>::value,
              "A must not be copy assignable");
static_assert(std::is_nothrow_move_constructible<A>::value,
              "A must be noexcept move constructible");
static_assert(std::is_nothrow_move_assignable<A>::value,
              "A must be noexcept move assignable");

上面我在一些特征中使用了_nothrow_。如果您想允许关联的特殊成员引发异常,请删除该部分。

【讨论】:

    【解决方案3】:

    我建议您很少需要这样做。而在少数需要这样做的情况下,明确声明哪些特殊功能被删除以及哪些被默认可能不是坏事。

    您可能需要显式定义或删除特殊成员函数的正常原因是您的类是某种资源管理类。例如,它有自己的指针,编译器无法知道该资源的所有权是什么。但是,正如Rule of Zero 文章中所述:

    C++ 允许我们将所有权策略封装成通用的可重用 类。这是重要的一点!大多数情况下,我们的所有权需要 可以通过“ownership-in-a-package”类来满足。

    标准中包含常见的“包中所有权”类 库:std::unique_ptr 和 std::shared_ptr。通过使用 自定义删除器对象,两者都已变得足够灵活,可以管理 几乎任何类型的资源。

    所以我们很少需要编写自己的资源管理类了。通常应该可以从已经拥有所有权的其他类构建一个类。然后默认的特殊成员函数应该如您所愿。

    例如,如果您有一个 std::unique_ptr 成员,那么您的类是隐式不可复制的,或者如果您有一个 const 成员,那么您的类是隐式不可分配的。

    也就是说,如果你确实需要明确地使一个类不可复制,@n.m. succinctly outlined the rules on when constructors/assignment operators are implicitly defined,所以你至少需要:

    A() = default;
    A(A&&) = default;
    A& operator=(A&&) = default;
    

    我同意你的观点,C++11 的表现力已经足够我们不再需要提升了。

    【讨论】:

      【解决方案4】:

      您可以避免定义已删除的复制 c'tor 和赋值运算符,使用 boost noncopyable

      这样,意图可能比使用“删除”关键字更加明确和清晰

      你可以像这样简单地使用它:

      #include <boost/utility.hpp>
      class A : boost::noncopyable {
      public:
          A () = default;
          A (A&&) = default;
          A& operator= (A&&) = default;
      };
      

      【讨论】:

      • 正如我在另一条评论中提到的,我不想使用 Boost。删除声明很清楚 - 这就是它们的用途。
      • @Xirdus 最好在问题正文中澄清一下,cmets 可能会通过
      • 问题是“如何声明默认构造函数”,而不是“如何使类可移动”。即使前者直接来自后者,它仍然不一样 - 因此,我认为在有问题的正文中提及 Boost 毫无意义。
      • Boost 在许多情况下是 C++11 标准未来添加的游乐场,C++ 标准委员会成员和专家在其中做出贡献。 BOOST_MOVABLE_BUT_NOT_COPYABLE 和类 boost::noncopyable 简化了可读性并节省了代码行。很可能其中至少有一个将符合未来的 c++ 标准。我想了解您为什么不想使用 BOOST 作为标准的补充。
      • 5 次?这个问题的重点是减少它!事实上,您只需要 2(对于不可移动类)或 3(对于可移动类)。我同意,C++ 在某些地方表现力过强和令人困惑——实际上我开始讨厌这种语言,尤其是在他们想在 C++14 中做的事情的上下文中。实际上,我正在寻找另一种语言来完成我未来的项目(特别是 D 和 Nimrod)——但就目前而言,C++ 远远优于它们,这要归功于可用的工具、库和支持页面的数量。我的意思是,对于游戏。对于原生 GUI 应用,我选择 C#。
      猜你喜欢
      • 1970-01-01
      • 2015-08-14
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      相关资源
      最近更新 更多