CRTP在C ++中调度11 [英] CRTP Dispatch in C++11

查看:117
本文介绍了CRTP在C ++中调度11的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有以下代码:

 模板< class Derived> 
class Base {
public:
virtual void foo_impl()= 0;
void foo(){
static_cast< Derived *>(this) - > foo_impl(); // A
(* static_cast< Derived *>(this))。foo_impl(); // B
}
};

class Derived:public Base< Derived> {
private:
void foo_impl(){
bar();
}
};

几个问题:



A生成虚函数调用?虽然我可以在互联网上找到的大多数建议这样做,对我来说,我不明白编译器如何可以做静态调度,考虑到一个指针Derived仍然可以指向一个类型Derived2的对象,其中Derived2:公开派生。



行B是否解决了我在上一点(如果适用)提出的问题?看起来像它会,考虑到现在的调用不是一个指针,因此使用*。将避免虚函数调用。但是如果编译器将解引用的转换作为引用类型,它仍然可以生成一个虚函数调用...在这种情况下,解决方法是什么?



添加foo_impl()的C ++ 11 final关键字改变了编译器在任何(或任何其他相关)情况下的行为。

解决方案

blockquote>

将A行生成一个虚拟函数调用?


foo_impl()是虚拟的, Derived 会覆盖它。即使中的 foo_impl()不是显式地标记为 virtual ,它在基类中,这足以使它成为一个虚函数。


B行是否解决了我在上一个问题(如果适用)中提出的问题?


。无论调用是在指针还是在引用上:编译器仍然不知道你是否正在调用一个实例上的函数 foo_impl()源自 派生或直接实例派生上的类。



查看我的意思:

 

code> #include< iostream>

using namespace std;

template< class Derived>
class Base {
public:
virtual void foo_impl()= 0;
void foo(){
static_cast< Derived *>(this) - > foo_impl();
(* static_cast< Derived *>(this))。foo_impl();
}
};

class Derived:public Base< Derived> {
public:
void foo_impl(){
cout< Derived :: foo_impl()<< endl
}
};

class MoreDerived:public派生的{
public:
void foo_impl(){
cout< MoreDerived :: foo_impl()< endl
}
};

int main()
{
MoreDerived d;
d.foo(); //将输出MoreDerived :: foo_impl()两次
}

/ p>


是否将C ++ 11 final关键字添加到 foo_impl()编译器会在(或任何其他相关)情况下执行吗?


理论上,是 final 关键字将使得不可能在 Derived 的子类中覆盖该函数。因此,当通过指向 Derived 的指针执行对 foo_impl()的函数调用时,编译器 / em>静态解析呼叫。但是,据我所知,C ++标准并不需要编译器这样做。



结论



在任何情况下,我相信你真正想要做的是不要声明 foo_impl 在基类中的所有函数。这通常是使用CRTP时的情况。此外,您还必须声明 Base code>如果您希望它访问派生私有函数 foo_impl )。否则,您可以使 foo_impl()公开。


Say I have the following code:

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl(); //A
      (*static_cast<Derived*>(this)).foo_impl(); //B
   }
};

class Derived : public Base<Derived> {
private:
   void foo_impl() {
      bar();
   }
};

A few questions:

Will line A generate a virtual function call? Although the majority of what I can find on the internet recommends doing things this way, to me I don't see how the compiler can do static dispatch considering that a pointer to Derived could still actually point to an object of type Derived2 where Derived2 : public Derived.

Does line B fix the issue I brought up in my previous point (if applicable)? It seems like it would, considering that now the call is not on a pointer any more and thus using *. would avoid a virtual function call. But if the compiler treats the dereferenced cast as a reference type, it could still generate a virtual function call... in that case, what is the workaround?

Does adding the C++11 final keyword to foo_impl() change how the compiler would act in either (or any other relevant) case?

解决方案

Will line A generate a virtual function call?

Yes. foo_impl() is virtual and Derived overrides it. Even though foo_impl() in Derived is not explicitly tagged as virtual, it is in the base class, and this is enough to make it a virtual function.

Does line B fix the issue I brought up in my previous point (if applicable)?

No. It does not matter if the call is on a pointer or on a reference: the compiler still won't know whether you are invoking the function foo_impl() on an instance of a class that derives from Derived, or on a direct instance of Derived. Thus, the call is performed through a vtable.

To see what I mean:

#include <iostream>

using namespace std;

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl();
      (*static_cast<Derived*>(this)).foo_impl();
   }
};

class Derived : public Base<Derived> {
public:
   void foo_impl() {
      cout << "Derived::foo_impl()" << endl;
   }
};

class MoreDerived : public Derived {
public:
   void foo_impl() {
      cout << "MoreDerived::foo_impl()" << endl;
   }
};

int main()
{
    MoreDerived d;
    d.foo(); // Will output "MoreDerived::foo_impl()" twice
}

Finally:

Does adding the C++11 final keyword to foo_impl() change how the compiler would act in either (or any other relevant) case?

In theory, yes. The final keyword would make it impossible to override that function in subclasses of Derived. Thus, when performing a function call to foo_impl() through a pointer to Derived, the compiler could resolve the call statically. However, to the best of my knowledge, compilers are not required to do so by the C++ Standard.

CONCLUSION:

In any case, I believe what you actually want to do is not to declare the foo_impl() function at all in the base class. This is normally the case when you use the CRTP. Additionally, you will have to declare class Base<Derived> a friend of Derived if you want it to access Derived's private function foo_impl(). Otherwise, you can make foo_impl() public.

这篇关于CRTP在C ++中调度11的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆