C++ 奇异递归模板模式(CRTP)
示例
CRTP是虚拟函数和传统继承的强大而静态的替代方法,可用于在编译时赋予类型属性。它具有一个基类模板,该模板将派生类作为其模板参数之一来工作。这允许它合法地执行static_cast其this指向派生类的指针。
当然,这也意味着CRTP类必须始终用作其他类的基类。并且派生类必须将自身传递给基类。
假设您有一组容器,都支持begin()和的功能end()。标准库对容器的要求需要更多功能。我们可以设计一个CRTP基类,该基类仅基于begin()和提供此功能end():
#include <iterator>
template <typename Sub>
class Container {
private:
//self()产生对派生类型的引用
Sub& self() { return *static_cast<Sub*>(this); }
Sub const& self() const { return *static_cast<Sub const*>(this); }
public:
decltype(auto) front() {
return *self().begin();
}
decltype(auto) back() {
return *std::prev(self().end());
}
decltype(auto) size() const {
return std::distance(self().begin(), self().end());
}
decltype(auto) operator[](std::size_t i) {
return *std::next(self().begin(), i);
}
};上述类提供的功能front(),back(),size(),和operator[]用于提供任何亚类begin()和end()。一个示例子类是一个简单的动态分配数组:
#include <memory>
//动态分配的数组
template <typename T>
class DynArray : public Container<DynArray<T>> {
public:
using Base = Container<DynArray<T>>;
DynArray(std::size_t size)
: size_{size},
data_{std::make_unique<T[]>(size_)}
{ }
T* begin() { return data_.get(); }
const T* begin() const { return data_.get(); }
T* end() { return data_.get() + size_; }
const T* end() const { return data_.get() + size_; }
private:
std::size_t size_;
std::unique_ptr<T[]> data_;
};DynArray该类的用户可以轻松地使用CRTP基类提供的接口,如下所示:
DynArray<int> arr(10); arr.front() = 2; arr[2] = 5; assert(arr.size() == 10);
有用性:这种模式特别避免了在运行时调用虚拟函数,因为虚拟函数会遍历继承层次结构,而仅依赖于静态强制转换:
DynArray<int> arr(10); DynArray<int>::Base & base = arr; base.begin(); //没有虚拟电话
begin()基类中函数内部唯一的静态转换Container<DynArray<int>>允许编译器极大地优化代码,并且在运行时不进行虚拟表查找。
局限性:因为基类是模板化的,并且对于两个不同的DynArrays是不同的,所以不可能将指向其基类的指针存储在类型均一的数组中,因为通常情况下,普通继承可以做到,即基类不依赖于派生类型:
class A {};
class B: public A{};
A* a = new B;