2

Introduction

Peter Weinhart describes how to design a generic intrusive_ptr base class using CRTP, which may be used as follows:

class foo : intrusive_base<foo>
{
     // foo-specific code.
};

This approach imposes the constraint that all foo objects carry a reference counter. Assume that we keep foo sometimes by value and only want to pay the price of the reference counter when we have a pointer. For example, sometimes we would like to create foo instances and just move them around, and sometimes we want to allocate foo on the heap.

Conceptually, the right mechanism for this scenario is a std::shared_ptr. However, there are certain scenarios requiring raw pointers that would call for an intrusive pointer, e.g., when passing pointers through a C API that take void pointers. In this case, one would "ref" the pointer before passing it to the opaque API and "unref" when getting it back.

Having control over foo, probably the best method would be to use a policy-based implementation and have a reference-counted and basic version of foo. Without having control over foo, an alternative design would to invert the inheritance relationship:

template <typename Base>
class intrusive : public Base
{
    // ?

private:
    std::atomic_size_t ref_count_;   
};

typedef intrusive<foo> intrusive_foo;

// Assume boost::intrusive_ptr as intrusive pointer implementation
boost::intrusive_ptr<intrusive_foo> x = new intrusive_foo;
{
    auto y = x;   // Semantics: boost::intrusive_ptr_add_ref(x.get())

    // At scope exit: boost::intrusive_ptr_release(x.get())
}

In the above mentioned article, Peter says that a such a "generic implementation of [intrusive] would make use of C++0x variadic templates and perfect forwarding."

Question

How would the implementation of such a generic intrusive class look like? I could see that it may benefit from C++11 inheriting constructors, but it is unclear to me how one would in fact implement the body of intrusive using the mentioned tools.

4

1 に答える 1

1

Using make_shared gives you the same efficiency as an intrusive pointer.

In this case, one would "ref" the pointer before passing it to the opaque API and "unref" when getting it back.

As someone else said, you can use enable_shared_from_this to get a shared_ptr back from a raw pointer (as long as there is at least one shared_ptr somewhere in the system which still owns the object)

But to answer the main question, I assume he means using variadic templates and perfect forwarding to define a constructor, which would look like:

template <typename Base>
  class intrusive : public Base
  {
    template<typename... Args>
      intrusive(Args&&... args)
      : Base(std::forward<Args>(args)...), ref_count_(0)
      { }

This allow you to construct the intrusive with any number of arguments of any type and they will be forwarded to the Base, so you can construct it with any arguments that could be used to construct a Base.

Another alternative would be to use C++11 inheriting constructors (which aren't implemented in any compiler AFAIK)

template <typename Base>
  class intrusive : public Base
  {
    using Base::Base;
于 2012-05-06T13:49:04.023 に答える