C++ 常数可以优化吗

C++ 常数可以优化吗,c++,optimization,memory-management,one-definition-rule,C++,Optimization,Memory Management,One Definition Rule,如果我们有一个静态常量类成员,其地址从未被使用过,那么该成员是否可以被优化掉并且不分配存储空间?,因为。而且,如果您正在生成一个共享库,这是不可能的。一般来说,最好假设对象可能会占用可执行文件中的空间 但是,不管是否提供了存储,如果初始化器在同一翻译单元中可见,那么您当然可以期望编译器内联使用该值。您可以通过以下方式观察到这一点:未能提供单独的定义,然后执行任何不涉及ODR使用的操作,并注意到可能不会出现未定义的引用链接错误。,因为。而且,如果您正在生成一个共享库,这是不可能的。一般来说,最好假

如果我们有一个静态常量类成员,其地址从未被使用过,那么该成员是否可以被优化掉并且不分配存储空间?

,因为。而且,如果您正在生成一个共享库,这是不可能的。一般来说,最好假设对象可能会占用可执行文件中的空间

但是,不管是否提供了存储,如果初始化器在同一翻译单元中可见,那么您当然可以期望编译器内联使用该值。您可以通过以下方式观察到这一点:未能提供单独的定义,然后执行任何不涉及ODR使用的操作,并注意到可能不会出现未定义的引用链接错误。

,因为。而且,如果您正在生成一个共享库,这是不可能的。一般来说,最好假设对象可能会占用可执行文件中的空间

但是,不管是否提供了存储,如果初始化器在同一翻译单元中可见,那么您当然可以期望编译器内联使用该值。您可以通过以下方式观察到这一点:未能提供单独的定义,然后执行任何不涉及ODR使用的操作,并注意可能不会出现未定义的引用链接错误。

答案是这取决于上下文

编译器必须遵循“仿佛”规则,这确保程序必须以与未优化程序相同的方式进行优化-因此它只能删除已知不会影响语法有效代码行为的常量

短版 简短的版本是,只有符合以下标准的常数才能优化:

常量是平凡的,或者具有不影响外部状态的可见构造函数/析构函数, 这种效果是可传递的。如果构造函数/析构函数调用一个不可见的函数,编译器必须假定该调用可能会改变外部状态,因此具有重要意义。 未使用ODR常量,并且 常量可以在未命名的命名空间中匿名定义,也可以内联定义 长版本 有几种情况会影响存储备份

是否已为常量提供显式存储支持, 常量是否存在于未命名的命名空间中, 常量是否具有非平凡、不可见的构造函数/析构函数, 常量是否在C++17中定义, 该常量是否在内联C++17中定义并使用ODR, 该常量是否在线定义,但未给定存储备份 让我们将这些不同的部分进行分解:

1.常量被赋予显式存储备份 如果从一开始就定义了存储备份,例如:

结构示例{ 静态常量int值; }; 常量int示例::值=5; 然后通常会有存储备份,因为编译器必须假设它最终将被ODR使用——即使常量是私有的

2.常量位于未命名的命名空间中 但是,如果这些类型是在未命名的命名空间中定义的,这使它们成为翻译单元的匿名符号,那么缺少使用可能会导致它被优化掉:

名称空间{ 结构示例{ 静态常量int值; }; 常量int示例::值=5; }

这只适用于普通构造函数/析构函数,或者编译器当前可以看到的构造函数/析构函数,以便优化out指令

3.常量位于具有非平凡构造函数/析构函数的未命名命名空间中 如果上述常量具有非平凡构造函数或析构函数,或不可见的构造函数/析构函数,则必须发出常量:

要么:

名称空间{ 结构参与者{ actor;//未定义 ~actor;//未定义 }; 课例{ 静态常量值; }; const-actor-example::value{}; } 或:

外部的、内部的、全局的; 名称空间{ 结构参与者{ 参与者{::some_global=5;} ~actor{::some_global=10;} }; 课例{ 静态常量值; }; const-actor-example::value{}; } 4.常量是在C++17中内联定义的 如果在C++17中定义内联静态const或内联静态constepr值,则它取决于是否使用ODR来表示是否会发出符号

无ODR使用-无排放:

结构示例{ 内联静态常量int值=5; }; 空隙试验 { std::printf%d,示例::值; }

5.常量是通过ODR使用内联C++17定义的 如果符号是内联的,但使用了ODR,则必须发出存储备份

ODR使用-排放:

结构示例{ 内联静态常量int值=5; }; void consumer int&; 空隙试验 { 消费者示例::值 }

6.常量是在行中定义的,但不提供存储备份 如果静态常量对象的内联定义没有C++17的内联,则永远不应该有存储备份。这些常量只能由带有 或者没有constexpr,但没有明确声明的存储支持—这意味着除非声明为内联,否则不能使用ODR

这就是C++的类型特征所利用的,因为它们不产生任何额外的代码,因为它们相当于编译时常量对象

结构示例{ 静态常数int值=5; }; 编辑:我想补充的另一个注意事项是:如果通过显式定义或通过ODR与内联符号一起使用,为具有非内部链接的符号指定了任何存储备份,则编译器/链接器应该无法优化此备份。至少,尽管某些非标准的优化标志可能允许这样做,但这并不是因为对“好像”规则的正确解释

问题是,编译器必须假设具有外部链接的符号仍然可以在其他地方命名或引用,即使该符号是私有的,并且永远不能通过朋友关系访问。C++有你能用的尖角。因此,即使它在逻辑上不应该是可访问的,但实际上它来自语言,因此,从编译器的角度来看。

答案是它取决于上下文

编译器必须遵循“仿佛”规则,这确保程序必须以与未优化程序相同的方式进行优化-因此它只能删除已知不会影响语法有效代码行为的常量

短版 简短的版本是,只有符合以下标准的常数才能优化:

常量是平凡的,或者具有不影响外部状态的可见构造函数/析构函数, 这种效果是可传递的。如果构造函数/析构函数调用一个不可见的函数,编译器必须假定该调用可能会改变外部状态,因此具有重要意义。 未使用ODR常量,并且 常量可以在未命名的命名空间中匿名定义,也可以内联定义 长版本 有几种情况会影响存储备份

是否已为常量提供显式存储支持, 常量是否存在于未命名的命名空间中, 常量是否具有非平凡、不可见的构造函数/析构函数, 常量是否在C++17中定义, 该常量是否在内联C++17中定义并使用ODR, 该常量是否在线定义,但未给定存储备份 让我们将这些不同的部分进行分解:

1.常量被赋予显式存储备份 如果从一开始就定义了存储备份,例如:

结构示例{ 静态常量int值; }; 常量int示例::值=5; 然后通常会有存储备份,因为编译器必须假设它最终将被ODR使用——即使常量是私有的

2.常量位于未命名的命名空间中 但是,如果这些类型是在未命名的命名空间中定义的,这使它们成为翻译单元的匿名符号,那么缺少使用可能会导致它被优化掉:

名称空间{ 结构示例{ 静态常量int值; }; 常量int示例::值=5; }

这只适用于普通构造函数/析构函数,或者编译器当前可以看到的构造函数/析构函数,以便优化out指令

3.常量位于具有非平凡构造函数/析构函数的未命名命名空间中 如果上述常量具有非平凡构造函数或析构函数,或不可见的构造函数/析构函数,则必须发出常量:

要么:

名称空间{ 结构参与者{ actor;//未定义 ~actor;//未定义 }; 课例{ 静态常量值; }; const-actor-example::value{}; } 或:

外部的、内部的、全局的; 名称空间{ 结构参与者{ 参与者{::some_global=5;} ~actor{::some_global=10;} }; 课例{ 静态常量值; }; const-actor-example::value{}; } 4.常量是在C++17中内联定义的 如果在C++17中定义内联静态const或内联静态constepr值,则它取决于是否使用ODR来表示是否会发出符号

无ODR使用-无排放:

结构示例{ 内联静态常量int值=5; }; 空隙试验 { std::printf%d,示例::值; }

5.常量是通过ODR使用内联C++17定义的 如果符号是内联的,但使用了ODR,则必须发出存储备份

ODR使用-排放:

结构示例{ 内联静态常量int值=5; }; void consumer int&; 空隙试验 { 消费者示例::值 }

6.常量是在行中定义的,但不提供存储备份 如果静态常量对象的内联定义没有C++17的内联,则永远不应该有存储备份。这些常量只能由带有或不带有constexpr的常量表达式创建,但没有显式声明的存储支持—这意味着除非声明为内联,否则不能使用ODR

这就是C++的类型特征所利用的,因为它们不会产生任何 附加代码,因为它们相当于编译时常量对象

结构示例{ 静态常数int值=5; }; 编辑:我想补充的另一个注意事项是:如果通过显式定义或通过ODR与内联符号一起使用,为具有非内部链接的符号指定了任何存储备份,则编译器/链接器应该无法优化此备份。至少,尽管某些非标准的优化标志可能允许这样做,但这并不是因为对“好像”规则的正确解释


问题是,编译器必须假设具有外部链接的符号仍然可以在其他地方命名或引用,即使该符号是私有的,并且永远不能通过朋友关系访问。C++有你能用的尖角。因此,即使它在逻辑上不应该是可访问的,但实际上它来自语言,因此,从编译器的角度来看。

根据g++的判断,我将假设是。如果删除某些内容不会改变程序的行为,答案通常是肯定的。一个从未使用过的常量可以被优化掉。在另一个视图中,由于未使用它,它将不会显示在代码中。@YvesDaoust:使用“仿佛”规则,我会说是。@YvesDaoust:这个问题有两个答案。如果其中任何一个答案令您满意,请记住选择一个正确的答案,或者留下关于如何改进答案的反馈。根据g++,我将假设是。如果删除某些内容不会改变程序的行为,则答案通常是肯定的。可以优化掉一个从未使用过的常量。在另一个视图中,由于未使用它,它将不会显示在代码中。@YvesDaoust:使用“仿佛”规则,我会说是。@YvesDaoust:这个问题有两个答案。如果他们中的任何一个回答让你满意,请记住选择一个正确的答案,或者留下关于如何改进答案的反馈。哎哟!谢谢你的广泛讨论。哦,我忘记了关于静态常量inlineTouch!谢谢你的广泛讨论。哦,我忘记了静态常量内联