C++: what is the Curiously-Recurring-Template-Pattern? and can Curiously-Recurring-Template-Pattern replace virtual functions?
我没有对这个问题的准确描述,所以我只是在问这是否可能(如果可能的话,其他一些信息会很好)。
一个程序员告诉我可以避免虚拟函数/多态性造成的运行时开销。他说,为了避免运行时开销,可以在名为"奇怪的"重复出现的"模板"模式中使用模板,该模式类似于:
1 2 3 4 | class Derived : public Base<Derived> { // ... implementation here }; |
这种奇怪的重复模板模式是如何工作的?
如何使用奇怪的重复模板模式来替代正常的虚拟函数/多态性?
我弄错了吗?
非常具体地说,可以使用CRTP代替具有虚拟函数的基类来实现模板方法模式,而无需虚拟函数调用开销。
对于虚拟函数,tmp如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class ProvidesMethod { protected: void Method() { // do something Part1(); // do something else Part2(); // do something final } private: virtual void Part1() = 0; virtual void Part2() = 0; }; class ExposesMethod : private ProvidesMethod { public: using ProvidesMethod::Method; private: void Part1() { // first part implementation } void Part2() { // second part implementation } }; |
对于CRTP,它看起来是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | template <typename Derived> class ProvidesMethod { protected: void Method() { // do something self().Part1(); // do something else self().Part2(); // do something final } private: Derived& self() { return *static_cast<Derived*>(this); } const Derived& self() const { return *static_cast<const Derived*>(this); } }; class ExposesMethod : private ProvidesMethod<ExposesMethod> { public: using ProvidesMethod<ExposesMethod>::Method; private: friend class ProvidesMethod<ExposesMethod>; void Part1() { // first part implementation } void Part2() { // second part implementation } }; |
如朱利安所说,这是CRTP。你应该查一下。但CRTP不能取代虚拟功能。如果它在特定情况下对您有用,那么您首先实际上并不需要虚拟函数。看,模板提供了编译时多态性。虚拟函数提供运行时多态性。如果您不知道在编译时将调用哪个重写,那么基于模板的解决方案将不起作用。如果您总是知道一个对象在运行时的实际类型,那么您不需要虚拟函数。
这被称为crtp(用于奇怪的重复模板模式),因此您可以查看它。
虽然我不知道它如何取代经典的多态性…
另一方面,在某些情况下,可以用模板替换类的复杂层次结构(更多信息,请参阅基于策略的设计),但这并不总是可能的…
我不知道你将如何使用模板来提供类似于虚拟函数的东西——这对我来说似乎很奇怪——当然,如果没有一系列的诡计,最终意味着实现你自己的虚拟函数版本而不是使用编译器提供的版本,我发现很难看到你如何使代码调用函数不知道对象是什么类型,并为该对象类型调用正确的函数。这就是虚拟函数的作用。
此外,从个人经验来看,虚拟函数的实际开销非常小,只有在非常极端的情况下才有区别(非虚拟函数变成内联的主要情况,非常小,调用函数的开销占总执行时间的很大一部分(这也意味着函数需要多次调用on才能产生显著的差异)。如果你必须以某种其他方式"计算出要做什么"(使用if语句或类似的东西)(而不是在"虚拟函数"上实现你自己的变体),那么会有更多的开销,但这只是在重新设计轮子,除非你的新轮子比现有轮子更好,否则这不是一个好主意。