【问题标题】:Call to constant pointer-to-member function not being inlined调用未内联的常量指向成员函数
【发布时间】:2013-10-05 11:04:47
【问题描述】:

现在,我知道不能保证内联,但是...

鉴于以下情况:

struct Base {
    virtual int f() = 0;
};

struct Derived : public Base {
    virtual int f() final override {
        return 42;
    }
};

extern Base* b;

我们有:

int main() {
    return static_cast<Derived*>(b)->f();
}

编译为:

main:
    movl    $42, %eax
    ret

然而……

int main() {
    return (static_cast<Derived*>(b)->*(&Derived::f))();
}

编译为:

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    b, %eax
    movl    (%eax), %edx
    movl    %eax, (%esp)
    call    *(%edx)
    leave
    ret

这真是令人难过。

为什么没有内联对 PMF 的调用? PMF 是一个常量表达式!

【问题讨论】:

标签: c++ c++11


【解决方案1】:

这里的问题是,在第一种情况下,基于类型的去虚拟化将间接调用变成了直接调用。 当您添加成员指针时,不能使用基于类型的去虚拟化(因为它通过前端向下传递到关于被调用的类型和虚拟方法的优化信息来工作,在这种情况下,这些信息并不容易知道)。 GCC 可以通过知道B 是一个类并且知道它的成员只有在它被构造后才能被调用,从而能够不断地将实际访问折叠到virutal table。目前它不做这样的分析。

我建议向GCC bugzilla 填写增强请求。

【讨论】:

    【解决方案2】:

    内联指向函数的指针并不总是可能的(除非编译器能够弄清楚指针实际指向的内容,这通常很困难,因此编译器可能会在您期望它之前“放弃”)。

    编辑

    扩展我的答案:所有编译器的首要任务是生成正确的代码(尽管有时这也不会发生!)。诸如内联函数之类的优化是编译器仅在“安全”时才会执行的操作。编译器可能不太“理解”上述表达式确实是一个常量表达式,因此退回到“让我们做安全的事情”(即通过虚函数表调用,而不是内联函数)。指向虚成员函数的指针在 C++ 中是一个相当棘手的问题。

    【讨论】:

    • @AlanStokes:你的解释是什么?
    • 编译器确实知道类型。我用static_cast 告诉它。这是安全的。我用final 和一个常量表达式 PMF 来告诉它。我错过了什么吗? (同样,我知道内联无法保证,但这似乎是一个简单的优化机会)
    • 我希望它将对常量 PMF 的调用“展开”为常规方法调用。相比之下,它确实在不在虚拟继承上下文中时这样做。
    • @Gabriel Garcia:将对象指针转换为特定指针类型不会告诉编译器对象的特定类型。事实上,它绝对不会告诉编译器。编译器能够优化第一次调用的事实与您的演员表无关。无论有没有演员,第一次通话仍然是虚拟通话。它被内联的唯一原因是编译器能够在编译时确定对象的动态类型。您可以从第一次调用中删除您的static_cast,您会看到编译器仍然内联第一次调用。
    • @Gabriel Garcia:第二个调用的语义更复杂。它依赖于两个“变量”而不是一个:1)对象的动态类型,2)指向成员的值。确实,在您的示例中,两者都可以在编译时轻松确定。但显然编译器认为它过于复杂而无法完全优化。
    【解决方案3】:

    这确实有点烦人,但也不足为奇。

    大多数编译器转换通过模式匹配工作:识别模式并应用转换。那么这可能没有优化的原因是什么?好吧,也许只是没有基于这种模式编写的转换。

    最具体地说,问题可能与&amp;Derived::f有关,gcc 和 clang 都使用特定的表示形式来表示指向虚函数的指针:它们使用指向执行虚解析的蹦床函数的指针.因此,可能只是这个蹦床函数嵌套太多,编译器无法看穿。

    【讨论】:

      猜你喜欢
      • 2012-12-13
      • 1970-01-01
      • 2012-12-16
      • 2011-08-15
      • 2014-10-03
      • 2013-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多