将内联函数作为参数

时间:2016-03-29 16:34:23

标签: c++ c++11

我想知道当函数作为文章传递时,C ++是否仍然会服从inline关键字。在以下示例中,每次onFrame循环中调用frame()时,是否会将while的新帧推入堆栈?

bool interrupt = false;

void run(std::function<void()> frame) {
    while(!interrupt) frame();
}

inline void onFrame() {
    // do something each frame
}

int main() {
    run(onFrame);
}

或者改变这个有什么影响?

void run(std::function<inline void()> frame) {
    while(!interrupt) frame();
}

如果您没有明确答案,可以帮我找到测试方法吗?可能使用内存地址或某种调试器?

3 个答案:

答案 0 :(得分:8)

如果必须通过std::function类型删除的调度来实现,那么编译器很难内联您的函数。它无论如何都可能发生,但你要尽可能地努力。你提议的替代方案(采用std::function<inline void()>论证)是不正确的。

如果您不需要删除类型,请不要使用类型擦除。 run()可以简单地采用任意的可调用:

template <class F>
void run(F frame) {
    while(!interrupt) frame();
}

muuch 更容易内联编译器。虽然,简单地使用inline函数本身并不能保证函数内联。请参阅this answer

另请注意,当您传递函数指针时,这也会降低内联的可能性,这很不方便。我试图在这里找到一个有一个很好的例子的答案,但在那之前,如果内联非常重要,将它包装成lambda可能是要走的路:

run([]{ onFrame(); });

答案 1 :(得分:3)

  

仍然遵守inline关键字......将新框架推送到堆栈

这不是inline关键字首先执行的操作(请参阅this question以获取广泛参考)。

假设,正如Barry所做的那样,你希望说服优化器内联你的函数调用(再次为了运气:这与inline关键字无关),函数模板+ lambda可能是要走的路。

要了解其原因,请考虑优化器在每种情况下必须使用的内容:

  1. 功能模板+ lambda

    template <typename F>
    void run(F frame) { while(!interrupt) frame(); }
    
    // ... call site ...
    run([]{ onFrame(); });
    

    这里,该函数仅在调用站点存在(从模板实例化),优化器需要在范围内工作并且定义良好。

    请注意,如果优化器认为额外的指令缓存压力超过堆栈帧的保存,则可能仍然合理地选择不内联调用

  2. 函数指针

    void run(void (*frame)()) { while(!interrupt) frame(); }
    
    // ... call site ...
    run(onFrame);
    

    这里,run可能必须编译为一个独立的函数(尽管如果它可以证明没有人使用它,链接器可能会丢弃该副本),onFrame也是如此,特别是因为它的地址。最后,在决定是否内联这些调用时,优化器可能需要考虑是否使用许多不同的函数指针调用run,或者只调用一个函数指针。总的来说,它似乎更多的工作,并可能最终作为链接时优化。

    NB。我使用“独立功能”来表示编译器可能会发出代码&amp;两种情况下正常自由函数的符号表条目。

  3. std::function

    这已经很久了。让我们注意到这个类很长( type erasure Barry提到)来实现这个功能

    void run(std::function<void()> frame);
    

    not 取决于函数的确切类型,这意味着在生成run的代码时隐藏编译器中的信息,这意味着优化器可以使用的更少(或者相反,需要做更多工作来撤消所有细心的信息隐藏)。

  4. 至于测试你的优化器所做的事情,你需要在整个程序的上下文中检查它:它可以根据代码大小和复杂性自由选择不同的启发式方法。

    要完全确定实际上做了什么,只需用源代码反汇编或编译成汇编程序。 (是的,这可能是一个很大的“公正”,但它是特定于平台的,而不是真正的问题主题,以及值得学习的技能)。

答案 2 :(得分:0)

编译发布并检查列表文件,或在调试器中打开反汇编。最好的方法是检查生成的代码。