Uncopyable class with automatic default and move constructors
Asked Answered
D

4

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?

Dinadinah answered 18/5, 2014 at 13:32 Comment(1)
Why do you want the class to be uncopyable? Is it because it has uncopyable members? In which case the class will be uncopyable anyway.Portillo
D
6

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)
Dextrality answered 18/5, 2014 at 13:32 Comment(5)
So, to sum it up - to make a class non-copyable-but-movable, I default the default constructor, move constructor and move assignment operator, and declare nothing else. Thanks!Dinadinah
I actually tested it and copy constructor is implicitly deleted when move constructor or move assignment operator is declared. TDM-GCC 4.8.1 gives nice error message:"'constexpr A::A(const A&)' is implicitly declared as deleted because 'A' declares a move constructor or move assignment operator"Dinadinah
@Dinadinah you are right... missed some rule, edited againFortuitous
@Dextrality how about using BOOST_MOVABLE_BUT_NOT_COPYABLE declaration in class scope?Ornelas
@hellfire769 I don't really want to pull in Boost for this project. So far, C++11 standard library has everything I need, and most probably what I would ever need.Dinadinah
U
2

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.

Udometer answered 18/5, 2014 at 16:0 Comment(0)
P
1

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.

Portillo answered 23/5, 2014 at 8:5 Comment(0)
D
0

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;
};
Dairying answered 18/5, 2014 at 13:32 Comment(7)
As I mentioned in the other comment, I don't want to use Boost. delete declarations are clear enough - that's what they are for.Dinadinah
@Dinadinah it would be better to clarify it in the body of the question, comments are likely to get passed throughOrnelas
The question is "how to declare default constructors", not "how to make class movable". Even if the former derives straight from the latter, it's still not the same - thus, I see no point in mentioning Boost in question body.Dinadinah
Boost is in many cases a playground for future additions to the C++11 standard, where C++ standard committee members and gurus place their contributions. BOOST_MOVABLE_BUT_NOT_COPYABLE and class boost::noncopyable ease readability and saves code lines. It is likely that at least one of them will be on future c++ standard. I would like to understand why you don't want to use BOOST as a complementary to the standard.Ornelas
for me, all caps identifiers hurt readability unconditionally, especially if they consist of five words divided with underscores. boost::noncopyable isn't necessarily better than A(const A&) = delete; - to someone who isn't familiar with the base class, this is black magic and word-of-mouth-promise until he checks the actual base class body - and language construct is obvious to anyone who is familiar with the language. As of why I don't want Boost - it's a bonus dependency, and that's always bad if you can avoid it. Even if it's almost-as-if-it-was-standard Boost library.Dinadinah
@Dinadinah if you don't want bonus dependency, it is reasonable, also the capital letters is not my taste either. I don't agree that writing 5 times "delete" "default" is clearer than a concrete single declaration. C++11 major is extensively borrowing idioms from other processing languages, such as python, and some functional programming languages, that are sophisticated and are very compact in coding. So if you don't want boost, you can construct those tools by yourself. C++ was (and still) not a very readable language. looking at other programming language may give better insights.Ornelas
5 times? The whole point of this question is to reduce it! And in fact you just need either 2 (for unmovable class) or 3 (for movable class). C++ is overly expressive and confusing in places, I agree - and I actually start to hate this language, especially in context of what they want to do in C++14. I'm actually looking for another language to do my future projects (in particular, D and Nimrod) - but for now, C++ is far superior to both of them, thanks to the number of tools, libraries and support pages available. I mean, for games. For native GUI apps I choose C#.Dinadinah

© 2022 - 2024 — McMap. All rights reserved.