CRTP避免动态多态
如何在 C++ 中使用 CRTP 以避免虚拟成员函数的开销?
How can I use CRTP in C++ to avoid the overhead of virtual member functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如何在 C++ 中使用 CRTP 以避免虚拟成员函数的开销?
How can I use CRTP in C++ to avoid the overhead of virtual member functions?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(5)
有两种方法。
第一个是为类型结构静态指定接口:
第二个是避免使用基址引用或基址指针习惯用法并在编译时进行连接。 使用上面的定义,您可以拥有如下所示的模板函数:
因此,将结构/接口定义和函数中的编译时类型推导结合起来,您可以进行静态分派而不是动态分派。 这就是静态多态性的本质。
There are two ways.
The first one is by specifying the interface statically for the structure of types:
The second one is by avoiding the use of the reference-to-base or pointer-to-base idiom and do the wiring at compile-time. Using the above definition, you can have template functions that look like these:
So combining the structure/interface definition and the compile-time type deduction in your functions allows you to do static dispatch instead of dynamic dispatch. This is the essence of static polymorphism.
我自己一直在寻找有关 CRTP 的不错的讨论。 Todd Veldhuizen 的科学 C++ 技术是一本很棒的书此资源 (1.3) 以及许多其他高级技术(例如表达式模板)。
另外,我发现您可以在 Google 图书上阅读 Coplien 的大部分原始 C++ Gems 文章。 也许情况仍然如此。
I've been looking for decent discussions of CRTP myself. Todd Veldhuizen's Techniques for Scientific C++ is a great resource for this (1.3) and many other advanced techniques like expression templates.
Also, I found that you could read most of Coplien's original C++ Gems article at Google books. Maybe that's still the case.
我必须查找 CRTP。 然而,完成此操作后,我发现了一些关于静态多态性的内容。 我怀疑这就是你问题的答案。
事实证明,ATL 相当广泛地使用了这种模式。
I had to look up CRTP. Having done that, however, I found some stuff about Static Polymorphism. I suspect that this is the answer to your question.
It turns out that ATL uses this pattern quite extensively.
采用严格签名检查的 CRTP/SFINAE 静态调度
这种静态调度解决方案使用了 CRTP 和 SFINAE,这并不新鲜。
该解决方案的独特之处在于它还强制执行严格的签名
检查,这允许我们在同一个静态中调度重载方法
动态调度适用于虚拟功能的方式。
首先,让我们首先看看使用传统解决方案的局限性
SFINAE。 以下内容摘自 Ben Deane 的 CppCon 2016 Lightning Talk
“虚拟函数的静态替代方案,使用表达式 SFINAE。”
使用上述代码,模板实例化
has_complete
一般来说,会做你所期望的事情。 如果
DerivedClass
有一个名为Complete
接受std::string
,结果类型将是std::true_type
。当你想重载一个函数时会发生什么?
在本例中,
Derived
实际上有一个名为foo
的方法,该方法接受bool
因为bool
可以隐式转换为int
。 所以,即使我们只为接受 bool 的签名设置调度,
has_foo
也会解析为std::true_type
,并且调用将是分派到
Derived::foo(int)
。 这是我们想要的吗? 可能不会,因为这不是虚拟函数的工作方式。 一个函数只能覆盖一个
如果两个签名完全匹配,则为虚函数。 我建议我们做一个
静态调度机制的行为方式相同。
这很好,但仅此并不能强制执行签名检查。 严格执行
签名检查,我们必须正确定义模板模板参数
操作
。 为此,我们将使用成员的std::integral_constant
函数指针。 看起来是这样的:
以这种方式定义我们的 Op 允许我们只分派到带有
签名完全匹配。
现在让我们把它们放在一起。
编写一个为非重载成员函数创建调度程序的宏
会很简单,但是制作一个支持重载函数的会
更具挑战性一点。 如果有人愿意贡献这一点,我欢迎
添加。
CRTP/SFINAE Static Dispatching with Strict Signature Checking
This solution for static dispatching uses CRTP and SFINAE, which is not new.
What is unique about this solution is that it also enforces strict signature
checking, which allows us to statically dispatch overloaded methods in the same
way dynamic dispatching works for virtual functions.
To begin, let's first look at the limitations of a traditional solution using
SFINAE. The following was taken from Ben Deane's CppCon 2016 Lightning Talk
“A Static Alternative to Virtual Functions, Using Expression SFINAE."
Using the above code, the template instantiation
has_complete<DerivedClass>
will, in general, do what you would expect. If
DerivedClass
has a method namedComplete
that accepts astd::string
, the resulting type will bestd::true_type
.What happens when you want to overload a function?
In this case,
Derived
does, in fact, have a method namedfoo
that accepts abool
becausebool
is implicitly convertible toint
. Therefore,even if we only set up dispatching for the signature that accepts a bool,
has_foo<Derived>
would resolve tostd::true_type
, and the call would bedispatched to
Derived::foo(int)
. Is this what we want? Probably not, becausethis is not the way that virtual functions work. A function can only override a
virtual function if the two signatures match exactly. I propose that we make a
static dispatch mechanism that behaves in the same way.
That's nice, but that alone doesn't enforce signature checks. To perform strict
signature checking, we have to properly define the template template parameter
Op
. To do this, we will make use of astd::integral_constant
of a memberfunction pointer. Here's what that looks like:
Defining our
Op
s in this way allows us to dispatch only to methods with anexact signature match.
Now let's put it all together.
Writing a macro that creates a dispatcher for a non-overloaded member function
would be simple enough, but making one that supports overloaded functions would
be a bit more challenging. If anybody cares to contribute that, I'd welcome the
addition.
这个维基百科答案拥有您所需的一切。 即:
虽然我不知道这实际上能给你带来多少钱。 虚拟函数调用的开销为(当然取决于编译器):
而 CRTP 静态多态性的开销为:
This Wikipedia answer has all you need. Namely:
Although I don't know how much this actually buys you. The overhead of a virtual function call is (compiler dependent, of course):
While the overhead of CRTP static polymorphism is: