5

I want to make some classes use automatically generated constructors, but be non-copyable (but still movable). Currently I'm doing it like this:

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

I wonder if it's really necessary to be so explicit. What if I wrote it like this:

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

Would it still work the same? What is the minimal set of defaults and deletes for other cases - non-copyable non-movable class, and class with virtual destructor?

Is there any test code I can use to quickly see which constructors are implicitly created?

4

4 回答 4

6

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

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

  • 默认移动构造函数和默认移动赋值运算符是隐式创建的,除非您声明了任何其他 Big 5 特殊函数(并且除非被不可移动成员或基阻止)
  • 除非您声明了任何构造函数(并且除非被不可默认创建的成员或基阻止),否则默认的默认构造函数(多么好的名字!)是隐式创建的
  • 除非您声明了移动构造函数或移动赋值运算符(并且除非被不可复制的成员或基阻止),否则将创建默认的复制构造函数和默认的复制赋值运算符
于 2014-05-18T13:51:01.467 回答
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_了一些特征。如果您想允许关联的特殊成员引发异常,请删除该部分。

于 2014-05-18T16:00:32.960 回答
1

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

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

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

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

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

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

也就是说,如果您确实需要明确地使类不可复制,@nm 简洁地概述了有关何时隐式定义构造函数/赋值运算符的规则,因此您至少需要:

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

我同意你的观点,C++11 的表现力足够强,我们不再需要为此进行提升。

于 2014-05-23T08:05:11.410 回答
0

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

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

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

#include <boost/utility.hpp>
class A : boost::noncopyable {
public:
    A () = default;
    A (A&&) = default;
    A& operator= (A&&) = default;
};
于 2014-05-20T14:18:30.703 回答