GCC的函数__attribute__s是否可以使用虚函数?

Ker*_* SB 11 c++ gcc pure-virtual

GCC C++编译器通过函数属性提供一系列扩展,例如:

int square(int) __attribute__((const));
Run Code Online (Sandbox Code Playgroud)

特别是两个属性,constpure允许您声明函数的评估没有副作用,并且仅依赖于其参数(const),或仅依赖于其参数和全局变量(pure).这允许公共子表达式消除,这可能导致这样的函数被调用的次数少于在代码中写入的次数.

我的问题是这是否可以安全,正确和合理地用于虚拟成员函数:

struct Foo
{
    virtual int square(int) __attribute__((pure));   // does that make sense?
};
Run Code Online (Sandbox Code Playgroud)

这有什么明智的语义吗?它是否允许?还是只是被忽略了?我担心在GCC文档中找不到答案.

这个问题的原因是存在一系列编译器选项-Wsuggest-attribute,这些选项使GCC产生关于可以放置这些属性以改进代码的建议.然而,它似乎最终甚至为虚拟功能提出了这些建议,我想知道是否应该认真对待这些建议.

Dam*_*mon 5

它被 GCC 允许和接受。它通常不会被忽略(你知道这是因为 GCC 总是输出warning: attribute ignored,当它完全忽略一个属性时,它不会在这里)。但也请阅读最后一段。

是否有意义是另一个问题。一个虚函数可以被重载,你可以在没有属性的情况下重载它。这就引出了下一个问题:合法吗?

人们会期望具有不同属性的函数具有不同的签名(例如带有const限定符或不同的异常规范),但事实并非如此。GCC 在这方面将它们视为完全相同。您可以通过获得验证这一点BarFoo贯彻成员函数非const。然后

decltype(&Bar::square) f1 = &Foo::square;
decltype(&Foo::square) f2 = &Bar::square;
Run Code Online (Sandbox Code Playgroud)

正如您所期望的那样,将在第二行中给出编译时错误,但不会在第一行中给出。如果签名不同(尝试使函数具有 const 限定,而不是使用属性!),第一行就会给出错误。

最后,它是否安全,是否有意义?它总是安全的,编译器必须确保它是安全的。在限制范围内,它在语义上确实有意义。

从语义的角度来看,声明一个函数是“正确的”,const或者pure如果这就是它的真正含义。但是,就您向界面的用户做出可能不正确的“承诺”而言,这有点尴尬。有人可能会调用这个函数,它似乎const在派生类上,但事实并非如此。编译器必须确保它仍然有效,但用户对性能的期望可能与现实有所不同。

将函数标记为constpure可能允许编译器更好地优化。现在,对于虚函数,这有点困难,因为对象可能是派生类型,而事实并非如此!
这必然意味着编译器必须忽略优化的属性,除非可以静态解析虚拟调用。这种情况可能仍然经常发生,但不是一般情况下。


jle*_*ahy 5

第一个问题是这些属性是否甚至具有虚拟方法的有效语义。在我看来,他们确实如此。我希望如果一个虚函数被标记为纯,你会向编译器承诺所有实现只依赖于它们的参数和全局内存中的数据(并且不要改变它),其中全局内存中的数据也将包括物体。如果一个虚函数被标记为 const 这意味着它只能依赖于它的参数,它甚至不允许检查对象的内容。编译器必须强制所有重写的虚拟方法声明属性至少与它们的父类一样强。

下一个问题是 GCC 是否使用这些属性进行优化。在下面的测试程序中,您可以看到版本 4.6.3 没有(尝试使用 -O3 编译为汇编程序,您将看到循环已展开)。

struct A {
    virtual int const_f(int x) __attribute__((const)) = 0;
};

int do_stuff(A *a) {
    int b = 0;
    for (int i=0; i<10; i++) {
        b += a->const_f(0);
    }
    return b;
}
Run Code Online (Sandbox Code Playgroud)

即使在编译时已知类型的以下程序中,编译器也不会优化循环。

struct A {
    virtual int const_f(int x) __attribute__((const)) = 0;
};

struct B : public A {
    int const_f(int x) __attribute__((const));
};

int do_stuff(B *b) {
    int c = 0;
    for (int i=0; i<10; i++) {
        c += b->const_f(0);
    }
    return c;
}
Run Code Online (Sandbox Code Playgroud)

从 A 中删除继承(从而使方法非虚拟)允许编译器进行预期的优化。

没有关于这些属性的标准或文档,因此我们可以拥有的最佳参考是实现。由于它们目前没有影响,我建议避免在虚拟方法上使用它们,以防将来行为发生意外变化。