没有在类中优化使用const静态变量?

时间:2015-10-08 08:16:30

标签: c++ static compiler-optimization

一个合理的合适的编译器可以丢弃这个const静态变量

class A{
     const static int a = 3;
}

如果在编译的二进制文件中没有使用它,或者它在二进制文件中是否显示?

2 个答案:

答案 0 :(得分:5)

简短回答:也许吧。该标准并未说明编译器是否保留常量(或字符串,函数或其他任何东西),如果它从未使用过。

答案很长:这在很大程度上取决于具体情况。如果编译器可以清楚地确定它没有被使用,它将删除未使用的常量。如果它无法做出该确定,则它不能删除未使用的常量,因为常量COULD可以由编译器当前不知道的东西使用(例如,另一个源文件)。

例如,如果class A在函数内部,编译器可以知道该类未在其他地方使用,如果该函数中未使用该常量,则不使用它任何地方。如果班级在"全球"空间,这样它可以在其他地方使用,然后它需要保持不变。

通过"整个程序优化"这会变得更加有趣。或者"链接时间优化" (从现在开始称为LTO),其中所有代码实际上都被优化为一个大块,当然,"使用"或"未使用"可以确定所有可能的用途。

您可以想象,结果还取决于编译器(以及LTO的链接器)的巧妙程度。所有编译器都应遵循&#34的原则;如果有疑问,请保留"。

您当然可以进行实验,并在使用变量的地方编写一些代码,然后删除使用,并查看它对汇编代码的不同之处(例如g++ -S x.cppclang++ -S x.cpp,以及看看生成的xs文件)。

答案 1 :(得分:1)

禁用优化时,答案取决于编译器。但是,当启用优化时,无论编译器如何,最终结果都是相同的。我们假设已启用优化。

当满足以下两个条件时,编译器不会为生成的目标文件中的const static字段发出定义:

  1. 它可以使用初始化的常量值来解析字段的所有用途。
  2. 最多有一个源代码文件使用了该字段(我将在最后讨论一个例外)。
  3. 我稍后会讨论第二个条件。但现在让我们专注于第一个。让我们看一个例子。假设目标平台是32位,并且我们已经定义了以下类型:

    // In MyClassA.h
    class MyClassA{
    public:
         const static int MyClassAField;
    };
    
    // In MyClassA.cpp (or in MyClassA.h if it was included in at most one cpp file)
    const int MyClassA::MyClassAField = 2;
    

    大多数编译器认为int是32位有符号整数。因此,在32位处理器上,大多数指令可以处理32位常量。在这种情况下,编译器将能够用常量2替换MyClassAField的任何用法,并且该字段将不存在于目标文件中。

    另一方面,如果字段的类型为double,则在32位平台上,指令无法处理64位值。在这种情况下,大多数编译器在目标文件中发出字段并使用SSE指令并从内存中注册到64位值并处理它们。

    现在我将解释第二个条件。如果有多个源代码文件正在使用该字段,则无法将其删除(无论是否启用Whole Program Optimization (WPO)),因为某个目标文件必须包含该字段的定义,以便链接器可以将它用于其他目标文件。但是,如果指定了正确的开关,则链接器可以从生成的二进制文件中删除该字段。

    这是一个链接器优化,其中VC ++为/OPT:REF,gcc为--gc-sections。对于icc,交换机的名称是相同的(Windows上的/ OPT:REF和Linx和OSX上的--gc-sections)。但是,编译器必须在目标文件的单独部分中发出每个函数和静态或全局字段,以便链接器可以消除它。

    然而,有一个问题。如果字段已内联定义如下:

    class MyClassA{
    public:
         const static int MyClassAField = 2;
    };
    

    然后编译器本身将从使用它的每个目标文件中删除该字段的定义。这是因为使用它的每个源代码文件都包含一个单独的定义。它们中的每一个都是单独编译的,编译器本身将使用称为常量传播的优化来优化该字段。实际上,即使禁用了优化,VC ++编译器也会执行此优化。

    当禁用优化时,const static字段是否将被删除取决于编译器,但可能不会被消除。