To prevent copying a class, you can very easily declare a private copy constructor / assignment operators. But you can also inherit boost::noncopyable.
What are the advantages / disadvantages of using boost in this case?
It makes the intent explicit and clear, otherwise one has to see the definition of the class,and search for the declaration related to copy-semantic, and then look for the access-specifier in which it is declared, in order to determine whether the class is noncopyable or not. Other way to discover it by writing code that requires copy-semantic enabled and see the compilation error.
The advantage is that you don't have to write a private copy constructor and a private copy operator yourself and it expresses clearly your intention without writing additional documentation.
"The traditional way to deal with these is to declare a private copy constructor and copy assignment, and then document why this is done. But deriving from noncopyable is simpler and clearer, and doesn't require additional documentation."
One concrete advantage (beyond expressing your intent slightly more clearly) is that the error will be caught sooner, at the compile stage not the link stage, if a member or friend function tries to copy an object. The base-class constructor/assignment are not accessible anywhere, giving a compile error.
It also prevents you accidentally defining the functions (i.e. typing {} instead of ;), a small error which may well go unnoticed, but which would then allow members and friends to make invalid copies of the object.
Advantages of boost::noncopyable over private copy methods:
It is more explicit and descriptive in the intent. Using private copy functions is an idiom that takes longer to spot than noncopyable.
It is less code / less typing / less clutter / less room for error (the easiest would be accidentally providing an implementation).
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.
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.
(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:
I'd rather use boost::noncopyable than manually delete or privatize the copy constructor and assignment operator.
However, I almost never use either method, because:
If I am making a non-copyable object, there has to be a reason it is non-copyable. This reason, 99% of the time, is because I have members that can't be copied meaningfully. Chances are, such members would also be better suited as private implementation details. So I make most such classes like this:
So now, I have a private implementation struct, and since I've used std::unique_ptr, my top-level class is non-copyable for free. The link errors that come from this are understandable because they talk about how you can't copy a std::unique_ptr. To me, this is all the benefits of boost::noncopyable and a private implementation rolled into one.
The benefit with this pattern is later, if I decide that I did indeed want to make my objects of this class copyable, I can just add and implement a copy constructor and/or assignment operator without changing the class hierarchy.
Under multiple inheritance, there can even be a space penalty:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
};
struct D
: public B,
public C,
private boost::noncopyable
{
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
For me this prints out:
3
But this, which I believe to have superior documentation:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
C(const C&) = delete;
C& operator=(const C&) = delete;
};
struct D
: public B,
public C
{
D(const D&) = delete;
D& operator=(const D&) = delete;
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
Outputs:
2
I find it much easier to declare my copy operations than to reason whether or not I'm deriving from boost::non_copyable multiple times and if that is going to cost me. Especially if I'm not the author of the complete inheritance hierarchy.
A small disadvantage (GCC specific) is that, if you compile your program with g++ -Weffc++ and you have classes containing pointers, e.g.
class C : boost::noncopyable
{
public:
C() : p(nullptr) {}
private:
int *p;
};
GCC doesn't understand what's happening:
warning: 'class C' has pointer data members [-Weffc++]
warning: but does not override 'C(const S&)' [-Weffc++]
warning: or 'operator=(const C&)' [-Weffc++]
While it won't complain with:
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class C
{
public:
C() : p(nullptr) {}
DISALLOW_COPY_AND_ASSIGN(C);
private:
int *p;
};
PS I know GCC's -Weffc++ has several issues. The code that checks for "problems" is pretty simplicistic, anyway... sometimes it helps.