What are the advantages of boost::noncopyable

三世轮回 提交于 2019-11-26 19:49:37

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.

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.

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
  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.

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.

Viktor Sehr

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

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.

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.

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.

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.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!