为什么 const class 成员必须是静态的才能正确优化?
Why must const class members be static to be properly optimised?
给定:
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
是不合适的。 Derived classes 通过构造函数将 x
初始化为不同的值;但是对于每个 classes x
是一个常量,并且可以优化那些 class 方法,就像前面的 static
案例一样,如果只评估 get()
到一个真正的常数。
事实上,我最常见的情况是在基 class:
中初始化引用
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()
在 getget()
中直接评估为 Bar::m
,我会避免指针间接级别。如果 x
是静态的,这将不可能。
我不清楚为什么需要 static
才能进行此优化。
在-class中初始化的static const int
成员是一个真正的常量表达式,即编译时常量。
非静态const int
成员在初始化后不能更改,但编译器很难静态地确定它只能有一个可能的值。请注意,非静态数据成员的 brace-or-equal-initializer 仅在该成员没有 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)
被调用。还要考虑如果成员是 public:
会发生什么
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
是静态数据成员。很可能编译器实现者根本懒得写这样的优化。
给定:
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
是不合适的。 Derived classes 通过构造函数将 x
初始化为不同的值;但是对于每个 classes x
是一个常量,并且可以优化那些 class 方法,就像前面的 static
案例一样,如果只评估 get()
到一个真正的常数。
事实上,我最常见的情况是在基 class:
中初始化引用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()
在 getget()
中直接评估为 Bar::m
,我会避免指针间接级别。如果 x
是静态的,这将不可能。
我不清楚为什么需要 static
才能进行此优化。
在-class中初始化的static const int
成员是一个真正的常量表达式,即编译时常量。
非静态const int
成员在初始化后不能更改,但编译器很难静态地确定它只能有一个可能的值。请注意,非静态数据成员的 brace-or-equal-initializer 仅在该成员没有 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)
被调用。还要考虑如果成员是 public:
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
是静态数据成员。很可能编译器实现者根本懒得写这样的优化。