【问题标题】:What is the difference between Strategy and CRTP for static polymorphism?静态多态的 Strategy 和 CRTP 有什么区别?
【发布时间】:2014-01-11 13:57:38
【问题描述】:

我希望有一个在编译时选择的具有多种可能实现的接口。我看到 CRTP 是实现这一点的首选习语。这是为什么?另一种方法是策略模式,但我没有看到任何地方提到过这种技术:

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 在这两种情况下都不会被编译器捕获,不幸的是,除非我真的尝试使用它。 Strategy 变体对我来说似乎更好,因为它避免了丑陋的static_cast,并且它使用组合而不是继承。还有什么 CRTP 允许的,策略不允许的吗?为什么主要使用 CRTP?

【问题讨论】:

  • 在 C++11 中,我可能会使用像 ((Impl*)this)-&gt;Implementation() 这样的 C 类型转换,因为它允许您使用 CRTP 类作为私有基础。
  • 你确定吗?我刚试了一下,还是不行。
  • This 是我的意思。根据 CRTP 基础的用例,您可能需要也可能不需要在基础中公开功能。
  • worksreinterpret_cast 也是如此。我通常尽量远离 C 风格的演员表。
  • 在 CRTP 中将static_cast&lt;Impl*&gt;(this) 替换为self(),并写入Impl* self() { return static_cast&lt;Impl*&gt;(this); }Impl const* self() const { return static_cast&lt;Impl*&gt;(this); }。在self() 方法中添加一些static_assert 以断言Impl 是一个子类。

标签: c++ design-patterns inheritance crtp static-polymorphism


【解决方案1】:

策略模式的通常实现与您的 CRTP 实现完全相同。基类定义了某种算法,将派生类中实现的部分排除在外。

所以 CRTP 实现了策略模式。您的 StrategyInterface 只是委托细节的实现,而不是策略模式的实现。

虽然您的两个实现都达到了相同的效果,但我更喜欢 CRTP,因为它可以利用可能的空基类优化。

【讨论】:

  • 实际上策略示例直接来自 GoF 书籍。我认为您是对的,尽管这两个示例实际上都以不同的方式实现了策略模式。
  • 所以如果我理解正确,唯一的区别是我的 Strategy 示例会产生更大的对象大小?
  • 是的,这是唯一的区别,我可以看到。
  • 不,OP 没有错过 CRTP 实现中的继承。由于您似乎不了解 CRTP,因此您可能不应该回答这个问题。
  • @Yakk 请善待并修复维基百科中损坏的描述:en.wikipedia.org/wiki/Curiously_recurring_template_pattern。特别是 class Derived : public Base&lt;Derived&gt; 部分。谢谢。
【解决方案2】:

除了静态多态之外,CRTP 还提供了覆盖基类函数的能力,因为它使用了继承机制。

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

如果使用 CRTP,派生类可以选择覆盖 genericFunc() 作为“特殊”实现,以防 genericFunc() 不适合。策略模式将无法提供正常继承带来的功能。

策略模式的一个优点是,如果 BasedInterface 需要在 Impl 中使用依赖类型,那将比 CRTP 容易得多。

template <class Impl>
class BaseInterface
{
    using SomeDerivedType = typename Impl::SomeType; 
}

【讨论】:

    猜你喜欢
    • 2016-08-29
    • 2013-07-21
    • 2016-10-21
    • 2016-08-31
    • 1970-01-01
    • 2014-03-30
    • 2011-04-23
    • 2014-07-29
    • 2018-11-23
    相关资源
    最近更新 更多