Lingxi
Lingxi

Reputation: 14977

Most concise way to disable copy and move semantics

The following surely works but is very tedious:

T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

I'm trying to discover the most concise way. Will the following work?

T& operator=(T) = delete;

Update

Note that I choose T& operator=(T) instead of T& operator=(const T&) or T& operator=(T&&), because it can serve both purposes.

Upvotes: 27

Views: 13598

Answers (6)

HolyBlackCat
HolyBlackCat

Reputation: 96326

It's enough to delete the copy operations, then the move operations will get removed automatically.


But if you're asking for "concise", according to this chart (by Howard Hinnant):

meow

The most concise way is to =delete move assignment operator (or move constructor, but it can cause problems mentioned in comments).

Though, as I said above, I wouldn't use this in practice, and would instead delete the copy constructor and copy assignment.

Upvotes: 48

einpoklum
einpoklum

Reputation: 131656

Please don't try to find "the most concise way" to write a piece of code.

If there isn't an obvious form of expressing something which is also very concise - don't try to language-lawyer your way into writing a few characters less. Why? Think of people reading your code: If you need to consult the standard to realize your code does what you want it to do - then so will your code's readers. Except that they won't know what you're trying to achieve; so they won't consult the standard; so they'll just be confused about what your code does. Or - some will get it and some wont.*

In your case, if you make a subset of these deletions, or use some other "clever" trick - as a person reading your code, it's somewhat likely I will not catch on, not notice you're actually trying to get all copy and move semantics deleted. And I'll get confused, thinking you're trying to do something else. In fact, if I were you, I'd even consider adding a comment saying:

/* Disabling copy and move semantics because XYZ */
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

which is even more "tedious", but would make your intention/motivation absolutely clear to your future readers.

There's also the question of what exactly the "XYZ" reason is. Some would argue that there's no good reason to delete the move members, and it's generally a bad idea to do so. C++ luminary Howard Hinnant has this to say on the matter.

* - A variant on a principle I spelled out here.

Upvotes: 5

einpoklum
einpoklum

Reputation: 131656

@ricab's answer suggested not inventing the wheel, and using boost::noncopyable as a mix-in base class. That does indeed work, but has the disadvantage of being confusing! ... as being non-copyable does not mean a class is non-movable. One would have to remember the historical circumstances for the naming of this boost class; most people sure don't, and neither do I.

Well, you could do the same, but renaming this class:

#include <boost/core/noncopyable.hpp>

namespace mixins {
using noncopyable_and_nonmovable = boost::noncopyable;
} // namespace mixins 

class X: private mixins::noncopyable_and_nonmovable {
  // etc. etc.
};

Now what you're doing is clear and obvious :-)

Also, you may want to consider creating a small namespace of mixins with this definition and possibly others (e.g. only non-copyable, only-non-movable etc.) into some utility header. That would make this solution even more concise to use.


I would still consider explaining the reason for the decision to disallow copying and moving somewhere; see my other answer.

Upvotes: 0

KevinZ
KevinZ

Reputation: 3309

I believe that in this case, macros are actually more readable:

#define NOT_COPYABLE( TypeName ) \
TypeName ( TypeName const& ) = delete; \
TypeName & operator = ( TypeName const& ) = delete;

#define NOT_MOVEABLE( TypeName ) \
TypeName ( TypeName && ) = delete; \
TypeName & operator = ( TypeName && ) = delete;

Upvotes: 3

ricab
ricab

Reputation: 2802

I prefer to inherit from boost::noncopyable, thereby making the intention immediately clear and delegating the details to a trustworthy library.

#include <boost/core/noncopyable.hpp>

class X: private boost::noncopyable
{
};

It involves adding a dependency, but if you are ok with that, it is arguably a very concise and expressive way to accomplish it.

Upvotes: 3

Vittorio Romeo
Vittorio Romeo

Reputation: 93304

You can write a simple struct and inherit from it:

struct crippled
{
    crippled() = default;

    crippled(const crippled&) = delete;
    crippled(crippled&&) = delete;

    crippled& operator=(const crippled&) = delete;
    crippled& operator=(crippled&&) = delete;
};

Usage:

struct my_class : crippled
{

};

int main()
{
    my_class a;
    auto b = a; // fails to compile
}

Upvotes: 3

Related Questions