简体   繁体   中英

What is the difference between Strategy and CRTP for static polymorphism?

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?

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.

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; 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM