c++ - 我可以在这里使用 Curiously Recurring Template Pattern (C++) 吗?

标签 c++ templates metaprogramming virtual crtp

我有一个 C++ 应用程序,可以简化为如下所示:

class AbstractWidget {
 public:
  virtual ~AbstractWidget() {}
  virtual void foo() {}
  virtual void bar() {}
  // (other virtual methods)
};

class WidgetCollection {
 private:
  vector<AbstractWidget*> widgets;

 public:
  void addWidget(AbstractWidget* widget) {
    widgets.push_back(widget);
  }

  void fooAll() {
    for (unsigned int i = 0; i < widgets.size(); i++) {
      widgets[i]->foo();
    }
  }

  void barAll() {
    for (unsigned int i = 0; i < widgets.size(); i++) {
      widgets[i]->bar();
    }
  }

  // (other *All() methods)
};

我的应用程序对性能至关重要。集合中通常有数千个小部件。从 AbstractWidget 派生的类(其中有几十个)通常不会覆盖许多虚函数。被覆盖的那些通常具有非常快的实现。

鉴于此,我觉得我可以通过一些巧妙的元编程来优化我的系统。目标是利用函数内联并避免虚函数调用,同时保持代码的可管理性。我研究了 Curiously Recurring Template Pattern(有关描述,请参阅 here)。这似乎几乎做我想做的事,但不完全是。

有什么方法可以让 CRTP 在这里为我工作吗?或者,有没有人能想到任何其他巧妙的解决方案?

最佳答案

模拟动态绑定(bind)(还有 CRTP 的其他用途)适用于基类认为自己是多态的,但客户端实际上只关心一个特定的派生类类(class)。因此,例如,您可能拥有表示某些特定于平台的功能的接口(interface)的类,并且任何给定的平台都只需要一个实现。该模式的要点是对基类进行模板化,这样即使有多个派生类,基类在编译时也知道正在使用哪一个。

当您真正需要运行时多态性时,例如当您有一个 AbstractWidget* 的容器时,它对您没有帮助。 ,每个元素可以是几个派生类之一,你必须迭代它们。在 CRTP(或任何模板代码)中,base<derived1>base<derived2>是不相关的类。因此也是derived1derived2 .除非它们有另一个公共(public)基类,否则它们之间没有动态多态性,但是你又回到了虚拟调用的起点。

您可以通过将 vector 替换为多个 vector 来获得一些加速:一个用于您知道的每个派生类,一个用于稍后添加新派生类且不更新容器时的通用 vector 。然后 addWidget 做一些(慢)typeid检查或虚拟调用小部件,将小部件添加到正确的容器中,并且当调用者知道运行时类时可能有一些重载。注意不要意外添加 WidgetIKnowAbout 的子类到 WidgetIKnowAbout* vector 。 fooAllbarAll可以循环遍历每个容器,依次(快速)调用非虚拟 fooImplbarImpl然后将被内联的函数。然后他们遍历希望小得多的 AbstractWidget* vector ,调用虚拟 foobar功能。

它有点困惑而且不是纯 OO,但如果几乎所有小部件都属于容器知道的类,那么您可能会看到性能提升。

请注意,如果大多数小部件属于您的容器不可能知道的类(例如,因为它们在不同的库中),那么您就不可能进行内联(除非您的动态链接器可以内联。我的不能)吨)。您可以通过摆弄成员函数指针来降低虚拟调用的开销,但 yield 几乎可以忽略不计,甚至是负数。虚拟调用的大部分开销都在调用本身,而不是虚拟查找,并且不会内联通过函数指针的调用。

换个角度看:如果要内联代码,则意味着不同类型的实际机器代码必须不同。这意味着您需要多个循环,或者一个带有开关的循环,因为根据从集合中提取的某些指针的类型,机器代码显然不能在每次通过循环时在 ROM 中更改。

好吧,我想也许对象可能包含一些 asm 代码,循环将其复制到 RAM 中,标记为可执行文件,然后跳入。但这不是 C++ 成员函数。而且它不能便携地完成。它甚至可能不会很快,复制和 icache 失效是什么。这就是存在虚拟调用的原因......

关于c++ - 我可以在这里使用 Curiously Recurring Template Pattern (C++) 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1002587/

相关文章:

c# - Windows 8.1 通用版本上的 Admob

c++ - 比较函数调用返回的字符串

c# - 如何在csharp中使用泛型类型继承

python - 自动将枚举成员的值设置为其名称

c++ - if constexpr 似乎只有在两种情况都有效的情况下才有效

c++ - C++ 中不明确的函数重载

c++ - eclipse 上交叉 GCC 和 MacOSX GCC 之间的区别

javascript - 如何在页面中添加HTML模板?

c++ - 与模板类中的友元函数链接错误

formatting - 不在 sympy 的 `printing.ccode` 方法中将 X*X*X 收缩为 pow(X,3)