4

我正在努力更好地了解 CRTP。到目前为止,我的理解是它允许编写如下函数。

template <class T>
void foo(Base<T> x ) { x.do_stuff() }

现在,根据x传递给函数的实际编译时派生对象foo(),它会做不同的事情。

但是,我可以从该类派生DerivedBase使用do_stuff()非虚拟但被覆盖的Derived::do_stuff. 因此,什么时候使用 CRTP 是正确的,而不是最简单的非平凡示例,它显示了 CRTP 相对于阴影/遮罩的优势。

4

1 回答 1

6

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 TC++0x 中的私有析构函数 +)。

于 2011-08-30T07:16:05.033 回答