CRTP与“派生的”函数中的函数的直接实现对比

发布于 2024-12-02 04:47:01 字数 403 浏览 2 评论 0原文

我正在尝试更好地了解 CRTP。到目前为止,我的理解是它允许人们编写如下所示的函数。

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

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

但是,我可以从 Base 派生类 Derived ,并使用非虚拟但重写的 屏蔽/遮蔽其 do_stuff()派生::do_stuff。因此,什么时候使用 CRTP 才是正确的,而是最简单的非平凡示例,它显示了 CRTP 相对于阴影/掩蔽的优势。

I am trying to get a better understanding of CRTP. So far my understanding is that it allows one to write a functions like the following.

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

Now, depending on the actual compile time derived object x that is passed to the function foo(), it will do different things.

However, I could have derived the class Derived from Base and masked/shadowed its do_stuff() with a non-virtual but overridden Derived::do_stuff. So when exactly is it correct to use CRTP, rather the simplest non-trivial example which shows the advantage of CRTP over shadowing/masking.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

宣告ˉ结束 2024-12-09 04:47:01

CRTP 的要点是能够在没有虚拟性的情况下获取派生对象的类型。如果您这样做

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

void bar(const B& x) { x.foo(); }

,那么当您传递 D 对象时,bar 会调用 B::foo 而不是 D::foo ,因为 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 对象传递给 bar 时,它会调用 D::foo()

另一种 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)。

The point of the CRTP is to be able to get the derived object's type without virtuality. If you do

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

void bar(const B& x) { x.foo(); }

then bar calls B::foo and not D::foo when you pass a D object, since foo isn't a virtual function. If you want D::foo to be called, then you need either virtual functions, or CRTP.

With the simplest kind of 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();
}

this calls D::foo() when you pass to bar a D object.

An alternative CRTP trick, which however forces D to provide an implementation for foo, is

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(); }

but you still need a template parameter for B (so that foo dispatches correctly), and therefore a template bar function.

Also, if you don't do CRTP, you'd better have a virtual destructor, which might add unwanted overhead for lightweight classes which are meant to be completely inlined. With CRTP you will simply write a protected destructor (private destructor + friend T in C++0x).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文