CRTP ( The Curiously Recurring Template Pattern)

Posted Ultraman_X

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CRTP ( The Curiously Recurring Template Pattern)相关的知识,希望对你有一定的参考价值。

CRTP ( The Curiously Recurring Template Pattern)

什么是CRTP

  • 继承自模板类
  • 子类将自己作为模板参数传递给父类

如下:

template <typename T>
class Base
{
    ...
};

class Derived : public Base<Derived>
{
    ...
};

这样的目的是在父类中可以使用子类。从父类的角度子类就是自己。父类通过static_cast将自己下降到子类来访问子类。

template<typename T>
class Base
{
public:
  void doSomething()
  {
    T& derived = static_cast<T&>(*this);
    // use derived...
  }
};

** 注意 ** 使用的是static_cast而不是dynamic_cast。

使用场景

一个类提供了一些函数,其他类也可以使用

例如有这样一个类Sensitivity代表了当给定一个值的时候,因为这个给定值的影响,他的产出是多少.

class Sensitivity
{
public:
  double getValue()const;
  void setValue(double value);
};

现在需要一些帮助函数,这样可以缩放或者开平方或者设置负值。

class Sensitivity
{
public:
  double getValue() const;
  void setValue(double value);

  void scale(double multiplicator)
  {
      setValue(getValue() * multiplicator);
  }
  void square()
  {
      setValue(getValue() * getValue());
  }
  void setToOpposite()
  {
      scale(-1);
  };

  // rest of the sensitivity\'s rich interface...
};

如果我们有另外一个类,同样有一个值,也需要这样三个帮助函数。我们使用CRTP,3个帮助函数可以放到另一个类中:

template <typename T>
struct NumericalFunctions
{
  void scale(double mult);
  void square();
  void setToOpposite();
};

class Sensitivity : public NumericalFunctions<Sensitivity>
{
public:
    double getValue() const;
    void setValue(double value);
    // rest of the sensitivity\'s rich interface...
};

这样,3个帮助函数需要从sensitivity访问到getValue和setValue这两个函数:


template <typename T>
struct NumericalFunctions
{
    void scale(double multiplicator)
    {
        T& underlying = static_cast<T&>( * this);
        underlying.setValue(underlying.getValue() * multiplicator);
    }
    void square()
    {
        T& underlying = static_cast<T&>( * this);
        underlying.setValue(underlying.getValue() * underlying.getValue());
    }
    void setToOpposite()
    {
        scale(-1);
    };
};

有趣的是虽然CRTP使用的是继承关系,但是和其他继承关系是有所不同的。父类并不是一个接口,子类并不是实现。父类使用子类的函数(getValue和setValue)。子类为父类提供了接口。

静态接口(static interfaces)

但并没有virtural关键字,而且是在编辑阶段进行的。

template <typename T>
class Amount
{
public:
    double getValue() const
    {
        return static_cast<T const&>(* this).getValue();
    }
};

有两个实现,一个返回值,一个设置值。

class Constant42 : public Amount<Constant42>
{
public:
    double getValue() const {return 42;}
};

class Variable : public Amount<Variable>
{
public:
    explicit Variable(int value) : value_(value) {}
    double getValue() const {return value_;}
private:
    int value_;
};

最后我们给客户端提供一个接口

template<typename T>
void print(Amount<T> const& amount)
{
    std::cout << amount.getValue() << \'\\n\';
}
Constant42 c42;
print(c42);
Variable v(43);
print(v);

以上是关于CRTP ( The Curiously Recurring Template Pattern)的主要内容,如果未能解决你的问题,请参考以下文章

基于 CRTP 的解决方案会是啥样子?

浅谈 CRTP:奇异递归模板模式

浅谈 CRTP:奇异递归模板模式

CRTP 中间类也需要成为 final

CRTP——访问不完整的类型成员

CRTP 和多级继承