What Are the Advantages of Boost::Noncopyable

What are the advantages of boost::noncopyable

Summarizing what others have said:

Advantages of boost::noncopyable over private copy methods:

  1. It is more explicit and descriptive in the intent. Using private copy functions is an idiom that takes longer to spot than noncopyable.
  2. It is less code / less typing / less clutter / less room for error (the easiest would be accidentally providing an implementation).
  3. It embeds meaning right in the type's metadata, similar to a C# attribute. You can now write a function which accepts only objects which are noncopyable.
  4. It potentially catches errors earlier in the build process. The error will be presented at compile-time rather than link-time, in the case that the class itself or friends of the class are doing the erroneous copying.
  5. (almost the same as #4) Prevents the class itself or friends of the class from calling the private copy methods.

Advantages of private copy methods over boost::noncopyable:

  1. No boost dependency

What are use cases for booster::noncopyable?

I find it useful whenever you have a class that has a pointer as a member variable which that class owns (ie is responsible for destroying). Unless you're using shared_ptr<> or some other reference-counted smart pointer, you can't safely copy or assign the class, because in the destructor you will want to delete the pointer. However, you don't know if a copy of the class has been taken and hence you'll get either a double-delete or access violation from dereferencing a freed pointer.

If you inherit from noncopyable then it has two benefits:

  • It prevents the class from being copied or assigned
  • It makes the intention clear from looking at the class definition, ie self-documenting code

eg

class MyClass : boost::noncopyable
{
...
};

Can a non-copyable member be used as alternative to make an object non-copyable?

Since C++11, the proper idiom for making a class non-copyable is to = delete the copy constructor/assignment operator. That's what C++ programmers are told to do, and that's what other C++ programmers will expect to see when looking for that behavior in your class..

It's fine to have a subobject (member or base class) that is non-copyable, and thus your default copy constructor/assignment operator will be implicitly deleted. But you should only do this for a subobject that happens to be non-copyable. That is, you have a unique_ptr<T> or mutex or whatever as a member because you need a unique_ptr<T> or a mutex as class instance data. Not because you're using it as a hack to make the type non-copyable.

The downsides of using a member subobject for this purpose are:

  1. It muddles the meaning of your code. Your mutex _dummy; example tells me that your type has a mutex in it. If nothing ever uses that variable, then that tells me that your code is rather incoherent; if you don't need a subobject, you don't declare one. = delete is the proper idiom, so it is what you should use.

  2. boost::noncopyable was the C++98/03 idiom because it was an empty class. And thus, common empty base optimization would ensure that it wouldn't take up any space in the derived class. Empty members get no such optimization, so a member boost::noncopyable will always make your class bigger, to no advantage. And while you could point to the upcoming C++20 [[no_unique_address]] attribute, see reason #1.

With explicitly deleted member functions in C++11, is it still worthwhile to inherit from a noncopyable base class?

Well, this:

private:
MyClass(const MyClass&) {}
MyClass& operator=(const MyClass&) {}

Still technically allows MyClass to be copied by members and friends. Sure, those types and functions are theoretically under your control, but the class is still copyable. At least with boost::noncopyable and = delete, nobody can copy the class.


I don't get why some people claim it's easier to make a class non-copyable in C++11.

It's not so much "easier" as "more easily digestible".

Consider this:

class MyClass
{
private:
MyClass(const MyClass&) {}
MyClass& operator=(const MyClass&) {}
};

If you are a C++ programmer who has read an introductory text on C++, but has little exposure to idiomatic C++ (ie: a lot of C++ programmers), this is... confusing. It declares copy constructors and copy assignment operators, but they're empty. So why declare them at all? Yes, they're private, but that only raises more questions: why make them private?

To understand why this prevents copying, you have to realize that by declaring them private, you make it so that non-members/friends cannot copy it. This is not immediately obvious to the novice. Nor is the error message that they will get when they try to copy it.

Now, compare it to the C++11 version:

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

What does it take to understand that this class cannot be copied? Nothing more than understanding what the = delete syntax means. Any book explaining the syntax rules of C++11 will tell you exactly what that does. The effect of this code is obvious to the inexperienced C++ user.

What's great about this idiom is that it becomes an idiom because it is the clearest, most obvious way to say exactly what you mean.

Even boost::noncopyable requires a bit more thought. Yes, it's called "noncopyable", so it is self-documenting. But if you've never seen it before, it raises questions. Why are you deriving from something that can't be copied? Why do my error messages talk about boost::noncopyable's copy constructor? Etc. Again, understanding the idiom requires more mental effort.

Noncopyable and Nonmovable together?

While a "noncopyable" will work, an "nonmovable" base class will not provide what you expect:

#include <utility>
#include <iostream>

struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable&) { std::cout << "copy\n"; }
nonmovable& operator = (const nonmovable&) { std::cout << "asign\n"; return *this; }
nonmovable(nonmovable&&) = delete;
nonmovable& operator = (nonmovable&&) = delete;
};

struct X : nonmovable {};

int main()
{
nonmovable n0;
nonmovable n1(n0);
// error: use of deleted function ‘nonmovable::nonmovable(nonmovable&&)’:
//nonmovable n2(std::move(n0));

X x0;
X x1(x0);
// However, X has a copy constructor not applying a move.
X x2(std::move(x0));
}

In addition, move construction and move assignment must be enabled explicitly after deletion of the copy constructor, if desiered:

struct noncopyable
{
noncopyable() = default;
// Deletion of copy constructor and copy assignment makes the class
// non-movable, too.
noncopyable(const noncopyable&) = delete;
noncopyable& operator = (const noncopyable&) = delete;

// Move construction and move assignment must be enabled explicitly, if desiered.
noncopyable(noncopyable&&) = default;
noncopyable& operator = (noncopyable&&) = default;
};

The, names "noncopyable" and "nonmovable" itself are good descriptive names. However, "boost::noncopyable" is both (non copyable and non movable), which might be a better (historical) design decision.

C++ how noncopyable works?

Default copy constructor and assignment operator are generated by the compiler in the derived class and not added by the programmer

The implicit functions will try to call their counterparts in the base class. This won't be possible since those are private to the base class, so you'll get a compilation error. This is how the base class is intended to work.

Copy constructor and assignment operator are defined and declared public in the derived class by the programmer

Then you've defeated the purpose of inheriting from the base class; your derived class is now copyable via these functions.

Copy constructor and assignment operator are defined and declared private in the derived class by the programmer

Again, you've defeated the base class and made your class copyable; but only within its member and friend functions.

Is it good practice to generally make heavyweight classes non-copyable?

Restricting your users isn't always a good idea. Just documenting that copying may be expensive is enough. If a user really wants to copy, then using the native syntax of C++ by providing a copy constructor is a much cleaner approach.

Therefore, I think the real answer depends on the context. Perhaps the real class you're writing (not the imaginary Shape) shouldn't be copied, perhaps it should. But as a general approach, I certainly can't say that one should discourage users from copying large objects by forcing them to use explicit method calls.



Related Topics



Leave a reply



Submit