我正在努力更好地了解CRTP。到目前为止,我的理解是它允许人们编写如下函数。
template <class T>
void foo(Base<T> x ) { x.do_stuff() }
现在,根据传递给函数x
的实际编译时派生对象foo()
,它将执行不同的操作。
但是,我可以从Derived
派生出类Base
,并使用非虚拟但被覆盖的do_stuff()
屏蔽/隐藏其Derived::do_stuff
。因此,当使用CRTP正确时,最简单的非平凡示例显示了CRTP优于阴影/屏蔽的优势。
答案 0 :(得分:6)
CRTP的目的是能够在没有虚拟性的情况下获取派生对象的类型。如果你这样做
struct B { void foo() const; }
struct D : B { void foo() const; }
void bar(const B& x) { x.foo(); }
然后bar
在您传递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();
}
当您传递给D::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,您只需编写一个受保护的析构函数(C ++ 0x中的私有析构函数+ friend T
)。