81

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?

manlio
  • 18,345
  • 14
  • 76
  • 126
tenfour
  • 36,141
  • 15
  • 83
  • 142

11 Answers11

59

I see no documentation benefit:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
};

vs:

struct A
{
     A(const A&) = delete;
     A& operator=(const A&) = delete;
};

When you add move-only types, I even see the documentation as misleading. The following two examples are not copyable, though they are movable:

#include <boost/noncopyable.hpp>

struct A
    : private boost::noncopyable
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

vs:

struct A
{
    A(A&&) = default;
    A& operator=(A&&) = default;
};

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 26
    To be fair, `boost::noncopyable` was available long before C++11 and compile support for `= delete`. I do agree with you that with C++11 near-compliant compilers, it is now obsolete. – Matthieu M. Feb 14 '12 at 08:09
  • 7
    Someone had a good idea and make the `noncopyable` a CRTP baseclass, so that all baseclasses in the hierarchy are unique. – Johannes Schaub - litb Mar 17 '12 at 12:40
  • 4
    Another disadvantage is that `private: __copy_constructor__;` is fully portable and you don't need ~40 MB of Boost dependencies. – Luis Machuca Oct 01 '13 at 06:02
  • 2
    This raises the question: What else in boost is made obsolete by C++11? – Jon Nov 05 '13 at 05:25
  • 3
    @Jon: there are no hard & fast answers to that question. However (just as an example) I would consider using `std::vector>` before reaching for `boost::ptr_vector` (http://www.boost.org/doc/libs/1_54_0/libs/ptr_container/doc/tutorial.html). Rationale: If I know `vector`, and I know `unique_ptr`, then I know the semantics of vectors of unique_ptr. And I know how the std::algorithms (e.g. sort) interact with it. I don't have to learn all about a new container with its member algorithms (e.g. member sort). – Howard Hinnant Nov 05 '13 at 15:08
  • 1
    I should've searched. http://stackoverflow.com/questions/8851670/relevant-boost-features-vs-c11 has a good list, although it's missing noncopyable and ptr_container. – Jon Nov 06 '13 at 01:21
  • @Jon: Thanks for the link. I hadn't seen that either. It is a good list and I've upvoted it. I agree with your comments concerning additions to it. – Howard Hinnant Nov 06 '13 at 01:29
  • Is there a way of detecting statically when the space penalty has been incurred? In the example above, clang and gcc produce the error `warning: direct base 'boost::noncopyable' (aka 'boost::noncopyable_::noncopyable') is inaccessible due to ambiguity`, but I don't know whether there are other pathological cases that would fail to trigger this warning. – Greg Nisbet Jul 23 '18 at 20:08
  • @GregoryNisbet: I'm not sure. I haven't bothered investigating that aspect. For me the answer would be irrelevant. – Howard Hinnant Jul 23 '18 at 22:15
  • 1
    your " The following two examples are not copyable, though they are movable:" example is wrong I think. I had to add one cosntructor for an example, but then it is not movable https://godbolt.org/z/7ezKhKzob. See also here https://stackoverflow.com/q/74827006/4117728 – 463035818_is_not_an_ai Dec 16 '22 at 16:29
  • You're right. The version without `boost::noncopyable` is movable, but the version with needs non-default move members. Yet another reason to dislike `boost::noncopyable`. – Howard Hinnant Dec 16 '22 at 18:05
48

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
tenfour
  • 36,141
  • 15
  • 83
  • 142
44

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.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 4
    You don't have to see the definition to see that a copy operator is private in the declaration. – spraff Oct 19 '11 at 15:44
  • 8
    @spraff: That is called *definition* of the class. A *definition* of class contains all the *declared* members. – Nawaz Oct 19 '11 at 15:46
  • 1
    To dig deeper, one part of the advantage of being explicit is that the meaning is now embedded in the typename metadata. Now you could write a function which accepts only noncopyable objects for example. – tenfour Oct 19 '11 at 15:50
  • 3
    If you don't have access to a class definition then it's an incomplete type and you can't really use it for *anything*. Without this definition, you can't see that it inherits `noncopyable` either. So it's a moot point. – spraff Oct 19 '11 at 15:50
  • Sure you can, using templates – tenfour Oct 19 '11 at 15:53
  • @spraff: There is a difference. It requires little effort to know the base clases of a class, than searching for the code related to copy-semantic, and then look for access-specifier in which it is *declared*. – Nawaz Oct 19 '11 at 15:55
  • I agree. Let's just not pretend that it's a *technical* difference (metaprogramming and C++11 notwithstanding). – spraff Oct 19 '11 at 15:59
  • 5
    @spraff: I don't understand what you mean by *technical* difference. Did I say anything of that sort? – Nawaz Oct 19 '11 at 16:01
17
  1. The intent of boost::noncopyable is clearer.
  2. Boost::noncopyable prevents the classes methods from accidentally using the private copy constructor.
  3. Less code with boost::noncopyable.
thiton
  • 35,651
  • 4
  • 70
  • 100
16

I can't understand why no one else seem to mention it, but:

With noncopyable you write the name of your class just once.

Without, fivefold duplication: One A for 'class A', two to disable the assignment, and two to disable the copy constructor.

ansgri
  • 2,126
  • 5
  • 25
  • 37
  • 1
    and you are saying it is not copyable, which increases readability and can be searched for. – hochl Apr 27 '16 at 15:10
10

Quoting the 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."

http://www.boost.org/libs/utility/utility.htm#Class_noncopyable

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Viktor Sehr
  • 12,825
  • 5
  • 58
  • 90
8

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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • @Mike: `...is that the error will be caught sooner, at the compile stage not the link stage`. How exactly? Even `boost::noncopyable` does the same thing which you would do if you don't use it. – Nawaz Oct 19 '11 at 15:51
  • @Nawaz: If you don't use a `noncopyable` base class, then you declare a private constructor in your class. That *is* accessible from the class's members and friends, so there is no compile error - just a link error due to the missing definition. (Unless you accidentally provide a definition - using a base class will prevent that mistake too). – Mike Seymour Oct 19 '11 at 15:54
  • 1
    Because noncopyable has *private* copy functions, they cannot be accessed by the child class at all - thus compiler error. If you put the functions in the child class, they can be accessed, and thus they are valid until the linker sees they're not defined. – tenfour Oct 19 '11 at 15:55
  • @MikeSeymour: Alright. It is about only members and friends. I didn't think about them. Nice point. But from practical point of view, it is almost no advantage, as modern IDE or so-called compiler does both in sequence, which means all you get errors. – Nawaz Oct 19 '11 at 15:58
  • @Nawaz: Yes, if you're building a simple program, then there's little advantage. If you're building a large project, then you might have a long wait until it tries to link. If you're building a library, then you won't get errors at all; they'll be deferred until you try to link that library into a program. – Mike Seymour Oct 19 '11 at 16:01
  • Accidentally providing an implementation for these private functions would be simple, and a big problem. Shutting down that possibility is a notable advantage. – tenfour Oct 19 '11 at 16:04
4

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.

manlio
  • 18,345
  • 14
  • 76
  • 126
3

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.

Nikko
  • 4,182
  • 1
  • 26
  • 44
2

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:

struct Whatever {
  Whatever();
  ~Whatever();
  private:
  struct Detail;
  std::unique_ptr<Detail> detail;
};

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.

wjl
  • 7,519
  • 2
  • 32
  • 41
1

The disavantage, according to Scott Meyers, the name is "non-natrual", if you do need to find a disavantage of it.

H Xu
  • 305
  • 1
  • 8