HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Static Multilevel Inheritance with CRTP (Fast Intrusive Pointers)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
fastwithintrusiveinheritancecrtppointersmultilevelstatic

Problem

I wanted to have a way to be able to static_cast to the proper derived type pointer of a base class to simulate a virtual call. Here the virtual call is for a reference counting base class:

template class ReferenceCounting


which should call the destructor of the TDerived type if the reference count drops to zero. This class achieves this by static_casting to its derived class:

delete static_cast(this)


Here we expose the CRTP pattern.

Let's do a quick example how that would work:

Deriving from this class, let's say:

class A : public ReferenceCounting{ ...code of A... }


and using a smart pointer (here an intrusion pointer), e.g. IntrusionPtr p( new A() ) lets the object be reference counted properly.
(if you are interested, the code is below, for smart pointers I refere to boost::shared_ptr and boost::intrusive_ptr)

Question for multiple inheritance using CRTP:

The question came up, what to do, if let's say we want to derive a class B from A and would like to use it also with an intrusive pointer, of course we would like that because class A inherits already from ReferenceCounting:

class B: public A { ...code... };


Using now an intrusive pointer naively like IntrusivePtr p(new B()) will TOTALY fail. Why? Because the class ReferenceCounting is instantiated with TDerived=A
which will then only call the destructor of A obviously.
One idea would to correct this behavior would be: We need a way to inject the proper derived type into ReferenceCounting (here that would be TDerived=B).
How to do this?

My answer can be found here: CRTP Multiple Inheritance.

I just give my implemented answer to the problem and try to explain it:

template
class A : public ReferenceCounting >::type                          
                                     >


The Select type trait injects the correct Derived type into ReferenceCounting, i.e A if PossibleDerived=void and PossibleDerived in any other

Solution

I have some causes for concern regarding this pattern.

For example:

class C : public B {
    int* ptr;
}


Forgetting to inject the CRTP parameter into a class that requires it will not give a compile error. It may not even give a run-time error. In this example it will just silently give you a memory leak that is a royal pain in the rear end to debug.

This will happily compile and give you a memory leak, even if C inherits properly from B with the CRTP parameters passed around:

IntrusivePtr a(new B()); 
IntrusivePtr b(new C());
a = b;


This basically prevents use of IntrusivePtr with polymorphic classes. But the compiler will happily accept such use and produce code that crashes at best or silently produces erroneous results/leaks memory at worst.

You may remember all the ins and outs of using this class every time but your next colleague may not even think about it and just go:


Cool, an intrusive pointer, I'll use it for this polymorphic code I'm writing.

Why do you not want to use a virtual destructor? This would make all your headaches go away. Yes you need a v-table, yes it's an extra indirection to figure out the correct destructor to call. But unless this is in the 1% of code that is your inner-most time critical loop, it will rarely matter on PC platforms.

Code Snippets

class C : public B {
    int* ptr;
}
IntrusivePtr<B> a(new B()); 
IntrusivePtr<B> b(new C());
a = b;

Context

StackExchange Code Review Q#54328, answer score: 3

Revisions (0)

No revisions yet.