10

I want to have an interface with multiple possible implementations, selected at compile-time. I saw that CRTP is the idiom of choice for implementing this. Why is that? An alternative is the Strategy pattern, but I see no mention of this technique anywhere:

template <class Impl>
class StrategyInterface
{
public:
    void Interface() { impl.Implementation(); }
    void BrokenInterface() { impl.BrokenImplementation(); }

private:
    Impl impl;
};

class StrategyImplementation
{
public:
    void Implementation() {}
};

template <class Impl>
class CrtpInterface
{
public:
    void Interface() { static_cast<Impl*>(this)->Implementation(); }
    void BrokenInterface() { static_cast<Impl*>(this)->BrokenImplementation(); }
};

class CrtpImplementation : public CrtpInterface<CrtpImplementation>
{
public:
    void Implementation() {}
};

StrategyInterface<StrategyImplementation> str;
CrtpImplementation crtp;

BrokenInterface is not caught by the compiler in either case, unfortunately, unless I actually try to use it. The Strategy variant seems better to me, as it avoids an ugly static_cast and it uses composition instead of inheritance. Is there anything else CRTP allows, that Strategy does not? Why is CRTP predominantly used instead?

Dan Nestor
  • 2,441
  • 1
  • 24
  • 45
  • In C++11 I would probably use a C cast like `((Impl*)this)->Implementation()` instead as it allows you to use the CRTP class as a private base. – Simple Jan 11 '14 at 14:33
  • Are you sure? I just tried it and it didn't work. – Dan Nestor Jan 11 '14 at 14:38
  • [This](http://ideone.com/2vKIUq) is what I meant. Depending on the use case of the CRTP base you may or may not need to make functions in the base public. – Simple Jan 11 '14 at 14:41
  • It [works](http://ideone.com/rDL25k) with `reinterpret_cast` as well. I usually try to stay away from C-style casts. – Dan Nestor Jan 11 '14 at 14:50
  • It actually compiles with `static_cast` too, if you include the template parameter in your `using` directive (on MSVC) – Dan Nestor Jan 11 '14 at 14:59
  • `reinterpret_cast` won't apply the base-to-derived pointer offsets though. C++11 has special wording that causes that C cast to do a `static_cast` but ignore accessibility. Do you have an example of the one with `static_cast`? – Simple Jan 11 '14 at 15:02
  • 1
    In CRTP replace `static_cast(this)` with `self()`, and write `Impl* self() { return static_cast(this); }` and `Impl const* self() const { return static_cast(this); }`. Add some `static_assert` to the `self()` methods as well to assert that `Impl` is a child class. – Yakk - Adam Nevraumont Jan 11 '14 at 15:04
  • http://ideone.com/vUUlEH - as I said it compiles on MSVC. – Dan Nestor Jan 11 '14 at 15:10
  • If that compiles on MSVC that would be a compiler bug. – Simple Jan 11 '14 at 15:12
  • Perhaps a Microsoft extension? – Dan Nestor Jan 11 '14 at 15:15
  • I found this site, it can compile with MSVC as well: http://rextester.com/POBVP97441 – Dan Nestor Jan 11 '14 at 21:26

2 Answers2

1

The usual implementation of the strategy pattern is exactly like your CRTP implementation. A base class defines some kind of algorithm, letting out some parts that are implemented in deriving classes.

So the CRTP implements the strategy pattern. Your StrategyInterface simply delegates the implementation of the details and is not an implementation of the strategy pattern.

While both of your implementations achieve the same effect, I would prefer the CRTP because it would take advantage of possible empty base class optimizations.

Torsten Robitzki
  • 3,041
  • 1
  • 21
  • 35
0

In addition to static polymorphism, CRTP provides the ability to overwrite base class functions due to the fact it uses inheritance mechanism.

template <class Impl>
class BaseInterface
{
    void genericFunc() { some implementation; } 
}

If use CRTP, the derived class could choose to overwrite genericFunc() as "special" implementation in case genericFunc() does not fit. Strategy patten will not be able to offer the functionality where normal inheritance brings in.

One advantage of the strategy pattern is that if BasedInterface needs to use dependent types inside Impl, that will be much easier than CRTP.

template <class Impl>
class BaseInterface
{
    using SomeDerivedType = typename Impl::SomeType; 
}
hefeicoder
  • 123
  • 1
  • 7