Xirdus
Xirdus

Reputation: 3087

Uncopyable class with automatic default and move constructors

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?

Upvotes: 5

Views: 1221

Answers (4)

Chris Drew
Chris Drew

Reputation: 15334

I would suggest you should rarely need to do this. And in the few circumstances where you do need to do it, explicitly declaring which special functions are deleted and which are defaulted may be no bad thing.

The normal reason why you might need to explicitly define or delete special member functions is if your class is some sort of resource managing class. For example it has owning pointers and there is no way of the compiler knowing what the ownership of that resource is. However, as is described in the Rule of Zero article:

C++ allows us to encapsulate ownership policies into generic reusable classes. This is the important bit! Most often, our ownership needs can be catered for by "ownership-in-a-package" classes.

Common "ownership-in-a-package" classes are included in the standard library: std::unique_ptr and std::shared_ptr. Through the use of custom deleter objects, both have been made flexible enough to manage virtually any kind of resource.

So it is very rare for us to need to write out own resource managing class anymore. It should normally be possible to build up a class from from other classes that already have the ownership baked in. And then the default special member functions should be as you expect.

For example, if you have a std::unique_ptr member then your class is implicitly uncopyable or if you have a const member then your class is implicitly unassignable.

That said, if you do need to explicitly make a class uncopyable, @n.m. succinctly outlined the rules on when constructors/assignment operators are implicitly defined and so you need at least:

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

And I agree with you that C++11 is expressive enough that we don't need boost for this any more.

Upvotes: 1

Daniel Heilper
Daniel Heilper

Reputation: 1250

You can avoid definition of deleted copy c'tor and assignment operator, using boost noncopyable

this way the intent may be even more explicit and clear, than using the "delete" keyword

you can simply use it like:

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

Upvotes: 0

Howard Hinnant
Howard Hinnant

Reputation: 218770

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

Yes. For example:

#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");

Above I've used _nothrow_ in some of the traits. Remove that part if you want to allow the associated special member to throw an exception.

Upvotes: 2

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

This will not work because no default constructor will be automatically created for you. No default constructor will be created because you have declared a copy constructor. It is defined as deleted, but it is user-declared nonetheless, so there is no implicitly defaulted default constructor.

The condensed rules for implicitly created constructors are:

  • The defaulted move constructor and the defaulted move assignment operator are created implicitly unless you have declared any other of the Big 5 special functions (and unless prevented by non-movable members or bases)
  • The defaulted default constructor (what a name!) is created implicitly unless you have declared any constructor (and unless prevented by non-default-creatable members or bases)
  • The defaulted copy constructor and the defaulted copy assignment operator are created unless you have declared the move constructor or the move assignment operator (and unless prevented by non-copyable members or bases)

Upvotes: 6

Related Questions