假设:
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(); }
编译输出提供内存提取以读取x
中fn0()
的值,而添加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::m
内的getget()
,我会避免指针间接的级别。如果x
是静态的,则无法执行此操作。
我不清楚为什么static
是必要的,以便进行此优化。
答案 0 :(得分:3)
在类中初始化的static const int
成员是一个真正的常量表达式,即编译时常量。
非静态const int
成员在初始化后无法更改,但编译器很难静态确定它只能有一个可能的值。请注意,仅当该成员没有 mem-initializer 时,才会使用非静态数据成员的大括号或等于初始值。这意味着,例如,如果你有这个:
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
是静态数据成员。很可能编译器实现者根本不愿意编写这样的优化。