答案是取决于上下文。
编译器必须遵循 as-if 规则,以确保程序的行为必须与未优化时的优化方式相同——因此它只能删除它知道不会影响语法有效代码行为的常量。
短版
简而言之,只有符合以下条件的常量才能被优化出来:
- 常量是微不足道的,或者具有可见的构造函数/析构函数,不影响外部状态,
- 此效果是可传递的。如果构造函数/析构函数调用了不可见的函数,编译器必须假设调用可能会改变外部状态,因此很重要。
- 不使用 ODR 常量,并且
- 常量要么是匿名定义的(在未命名的命名空间中),要么是内联定义的
加长版
有几种情况会影响存储备份
- 是否已为常量提供显式存储支持,
- 常量是否存在于未命名的命名空间中,
- 常量是否具有非平凡、不可见构造函数/析构函数,
- 常量是否定义
inline (C++17),
- 是否定义了常量
inline (C++17) 并使用了ODR,
- 常量是否内联定义,但未提供存储支持
让我们分解这些不同的部分:
1。常量有明确的存储支持
如果您从一开始就定义存储支持,例如:
struct example {
static const int value;
};
const int example::value = 5;
然后通常会有存储支持,因为编译器必须假设它最终会被 ODR 使用——即使常量是私有的。
Example on Compiler Explorer
2。常量位于未命名的命名空间中
但是,如果这些类型是在未命名的命名空间中定义的,这使它们成为翻译单元的匿名符号——那么缺乏使用可能会导致它被优化掉:
namespace {
struct example {
static const int value;
};
const int example::value = 5;
}
Example on Compiler Explorer.
这仅适用于普通的构造函数/析构函数,或编译器当前可以看到的构造函数/析构函数以优化指令。
3。常量在一个未命名的命名空间中具有非平凡的构造函数/析构函数
如果上面的常量有一个非平凡的构造函数或析构函数,或者一个不可见的构造函数/析构函数,则必须发出常量:
要么:
namespace {
struct actor{
actor(); // not defined
~actor(); // not defined
};
class example {
static const actor value;
};
const actor example::value{};
}
Example on Compiler Explorer
或:
extern int some_global;
namespace {
struct actor{
actor(){ ::some_global = 5;}
~actor(){ ::some_global = 10;}
};
class example {
static const actor value;
};
const actor example::value{};
}
Example on Compiler Explorer
4。常量定义为inline (C++17)
如果您在 C++17 中定义了 inline static const 或 inline static constexpr 值,则取决于它是否用于 ODR 来确定是否会发出符号。
无 ODR 使用 -- 无排放:
struct example {
inline static const int value = 5;
};
void test()
{
std::printf("%d", example::value);
}
Example on Compiler Explorer.
5。使用 ODR 定义常量 inline (C++17)
如果符号是 inline 但使用了 ODR,则必须发出存储支持。
ODR 使用 -- 排放:
struct example {
inline static const int value = 5;
};
void consume(const int&);
void test()
{
consume(example::value)
}
Example on Compiler Explorer.
6。常量是内联定义的,但没有提供存储支持
如果您有静态 const 对象的内联定义(没有 C++17 的 inline),则永远不应该有存储支持。常量只能由常量表达式创建(有或没有constexpr),但没有明确声明的存储支持——这意味着除非声明inline,否则它们不能用于ODR。
这是 C++ 的类型特征所利用的,因为它们不产生任何额外的代码,因为它们相当于编译时常量对象。
struct example {
static const int value = 5;
};
Example on Compiler Explorer
编辑:我想再补充一点:如果通过显式定义或通过使用 @987654347 的 ODR 为具有非内部链接的符号指定了任何存储支持@ 符号,编译器/链接器应该无法优化此退出。至少,不是来自对 as-if 规则的正确解释(尽管某些非标准优化标志可能允许这样做)。
问题在于编译器必须假设具有外部链接的符号仍然可以在其他地方命名或引用,即使该符号是private 并且永远无法通过friend-ship 访问。 C++ 有尖角,你可以reference private members in template parameters via explicit template specializations。因此,即使它在逻辑上不应该被访问,它实际上是从语言(以及编译器)的角度来看的。