2015-05-14 53 views
4

已知:为什么const类成员必须是静态的才能正确优化?

class Foo {  const int x = 5; public: inline int get() { return x; } }; 
class Bar { static const int x = 5; public: inline int get() { return x; } }; 
int fn0(Foo& f) { return f.get(); } 
int fn1(Bar& b) { return b.get(); } 

编译的输出给出了一个存储器提取在fn0()读取的x的值,而添加static结果字面5被内联在fn1()。暗示的是get()的调用者可以被优化,就好像它仅在整数常量是静态时使用常量来代替get()

我有更复杂的情况,其中static不合适。派生类通过构造函数将x初始化为不同的值;但对于每个类x是一个常数,那些类的方法可以优化,如前static的情况下,如果只有get()评估真正的常数。

其实,我最常见的情况是在基类中引用的初始化:

class Foo { int& x; public: Foo(int& init) : x(init) {} inline int get() { return x; } }; 
class Bar : public Foo { int m; public: Bar() : Foo(m) {} inline int getget() { return get(); }; 
int fn1(Bar& b) { return b.getget(); } 

这里,如果get()直接评估,以Bar::mgetget()内我会避免间接指针的水平。如果x是静态的,这将不可能。

我不清楚为什么static是必要的,以允许此优化。

+0

因此,简而言之,有一个答案指出'静态'成员可以放置在只读段中,因此它们的const属性由硬件强制执行。即使这不是根据规范的答案,我确实认为这是值得注意的。 – sh1

回答

3

A static const int在类中初始化的成员是一个真正的常量表达式,也就是编译时常量。

非静态const int成员在初始化后无法更改,但编译器难以静态确定它只能有一个可能的值。请注意,仅当该成员没有成组初始化程序时,才会使用非静态数据成员的括号或等值初始化程序。这意味着,例如,如果你有这样的:

class Foo { 
    const int x = 5; 
    public: 
    inline int get() { return x; } 
    Foo() = default; 
    Foo(int x) : x(x) {} 
}; 

然后Foo::x可能是5,如果默认构造函数被调用,也可能是别的东西,如果Foo::Foo(int)被调用。还要考虑如果成员由公众发生了什么:

class Foo { 
    public: 
    const int x = 5; 
    inline int get() { return x; } 
}; 

现在,可以使用集合初始化:

Foo f {42}; 
// f.x is 42 

在你写的Foo的特定情况下,我相信这是事实, Foo::x只能是5,但编译器确定这一点并不容易,因为Foo::x是静态数据成员。编译器实现者很可能根本就不会编写这样的优化。

相关问题