具有自动默认和移动构造函数的Uncopyable类

时间:2014-05-18 13:32:13

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

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

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;
}

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

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

4 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分: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_。如果要允许关联的特殊成员抛出异常,请删除该部分。

答案 2 :(得分:1)

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

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

  

C ++允许我们将所有权策略封装到通用可重用中   类。这是重要的一点!通常,我们的所有权需求   可以通过“所有权一揽子”课程来满足。

     

标准中包含常见的“一揽子所有权”类   library: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的表现力足以让我们不再需要提升。

答案 3 :(得分:0)

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

这种方式可能比使用“删除”关键字

更明确和清晰

你可以简单地使用它:

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