CRTP与"派生"中函数的直接实现

san*_*san 4 c++ templates crtp

我想要更好地了解CRTP.到目前为止,我的理解是它允许人们编写如下的函数.

template <class T>
void foo(Base<T> x ) { x.do_stuff() }
Run Code Online (Sandbox Code Playgroud)

现在,根据x传递给函数的实际编译时派生对象,foo()它将执行不同的操作.

但是,我可以Derived从非虚拟但被覆盖的派生类Base和掩盖/阴影它.因此,当使用CRTP完全正确时,更确切地说是最简单的非平凡示例,它显示了CRTP优于阴影/屏蔽的优势.do_stuff()Derived::do_stuff

Ale*_* C. 6

CRTP的目的是能够在没有虚拟的情况下获取派生对象的类型.如果你这样做

struct B { void foo() const; }
struct D : B { void foo() const; }

void bar(const B& x) { x.foo(); }
Run Code Online (Sandbox Code Playgroud)

然后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();
}
Run Code Online (Sandbox Code Playgroud)

这就要求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(); }
Run Code Online (Sandbox Code Playgroud)

但是你仍然需要一个模板参数B(以便foo正确调度),因此需要一个模板bar函数.

此外,如果你不做CRTP,你最好有一个虚拟析构函数,这可能会为轻量级类增加不必要的开销,这些类本意味着完全内联.使用CRTP,您只需编写一个受保护的析构函数(私有析构函数+ friend T在C++ 0x中).