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

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

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

但是,我可以从 Derived 派生类 Base 并使用非虚拟但覆盖的 do_stuff() 屏蔽/隐藏其 Derived::do_stuff 。所以什么时候使用 CRTP 是正确的,而不是最简单的非平凡示例,它显示了 CRTP 优于阴影/屏蔽的优势。

最佳答案

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,您只需编写一个 protected 析构函数(C++0x 中的私有(private)析构函数 + friend T)。

关于c++ - CRTP 与 "derived"中函数的直接实现,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7239771/

10-13 03:26