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