CRTP 的重点是能够在没有虚拟性的情况下获得派生对象的类型。如果你这样做
struct B { void foo() const; }
struct D : B { void foo() const; }
void bar(const B& x) { x.foo(); }
thenbar
调用B::foo
而不是D::foo
在传递D
对象时调用,因为foo
它不是虚函数。如果您想D::foo
被调用,那么您需要虚拟功能或 CRTP。
使用最简单的 CRTP:
template <typename>
struct B { void foo() const; }
struct D : B<D> { void foo() const; }
template <typename T>
void bar(const B<T>& x)
{
static_cast<const T&>(x).foo();
}
thisD::foo()
在传递给对象时bar
调用D
。
一个替代的 CRTP 技巧,然而,它强制D
为 提供一个实现foo
,是
template <typename T>
struct B
{
void foo() const { static_cast<const T*>(this)->foo_impl(); }
// default implementation if needed
// void foo_impl() const { ... }
};
struct D : B<D> { void foo_impl() const { ... } };
template <typename T>
void bar(const B<T>& x) { x.foo(); }
但是您仍然需要一个模板参数B
(以便foo
正确调度),因此需要一个模板bar
函数。
此外,如果您不执行 CRTP,则最好有一个虚拟析构函数,这可能会为要完全内联的轻量级类增加不必要的开销。使用 CRTP,您只需编写一个受保护的析构函数(friend T
C++0x 中的私有析构函数 +)。