包含在许多翻译单元中的静态常量的开销?

时间:2016-01-12 09:53:42

标签: c++ static header constants

在头文件中,可以在一行中声明和(预)定义全局常量。

// constants.h
namespace Constant{
    static const unsigned int framerate = 60;
    static const char * const windowName = "Test";
    static const unsigned char * const cursorBitmap = { lots of data };
}

我喜欢这种格式,因为它允许我将常量保存在一个位置,并且避免需要在一个文件中声明常量并在另一个文件中定义它,从而有助于提高可读性。但是,当任何翻译单元包含constants.h时,它会扩展这些定义,每个单元

我的问题是,如果我将constants.h包含在许多翻译单元中,这会导致很大的开销,例如,cursorBitmap和其他数组常量非常大吗?如果我将它包含在100个单元中,我的程序是否包含每个字符串和数组文字的100个副本?或者只会复制指针和值?

如果有开销,有没有办法可以避免它而不需要单独声明和定义?

(我也猜测'静态'在这种情况下是多余的,但我还是喜欢把它放在那里)

3 个答案:

答案 0 :(得分:3)

在各种翻译单元中是否重复字符串文字是一个实施质量问题。

直接声明的对象将在包含此标头的每个翻译单元中重复。虽然不是很多。并且在不直接或间接使用某个常量地址的转换单元中,它可以被优化掉。

如果您只想确保每个常量的一个副本,甚至没有副本,那么您可以使用类模板,如下所示:

constants.h
#pragma once

template< class Dummy >
struct Constant_{
    static const unsigned int framerate;
    static const char * const windowName;
    static const unsigned char * const cursorBitmap;
};

template< class Dummy >
const unsigned int Constant_<Dummy>::framerate = 60;

template< class Dummy >
const char * const Constant_<Dummy::windowName = "Test";

template< class Dummy >
const unsigned char * const Constant_<Dummy>::cursorBitmap = ...;

using Constant = Constant_<void>;

但这是恕我直言,而不是它的价值。

类似的替代方案是使用inline函数,每个常量一个。

答案 1 :(得分:1)

  

如果我将程序包含在100个单位中,我的程序是否包含每个字符串和数组文字的100个副本?或者只会复制指针和值?

标准不承诺合并字符串文字,因此由实现决定。在GNU / Linux上使用GCC 5.1.1进行的简单测试表明,字符串文字在未经优化的构建中合并,但在使用-O-Os时进行合并。但这只是合并实际的char数组。在某种程度上,编译器不会优化指针的存储,或者数值常量(如果它们是翻译单元的本地,而不是ODR使用的,以及POD类型,它们是明显的候选消除然而,as-if规则)编译器可能无法轻松地合并它们。该标准要求它们是不同的对象,因此必须具有不同的地址。 as-if规则可能仍然允许删除它们,即使你要使用它们的地址,但这通常需要全局程序优化,即链接时优化,包括库,实现可能不支持,仅以有限的方式支持,和/或仅取决于编译器和链接器设置。换句话说,它可能只是在适当的情况下发生,但更有可能不会发生。

我自己的测试表明GCC 5.1.1没有合并static const unsigned int ref公开的const个对象,即使是-Os -flto(优化大小并启用链接时优化)。如果任何当代实施确实执行了这种困难和模糊的优化,我会坦率地感到惊讶。

  

(我也猜测'静态'在这个用法中是多余的,但我还是喜欢把它放在那里)

如果您有多个翻译单元,那么这并不是多余的,因为否则会违反一个定义规则(ODR)。但是,作为旁注,在命名空间范围内static已被认为在语法上已经过时很久了(考虑使用在C ++ 98中引入的匿名命名空间)。

(响应Cheers和hth。 - Alf)

  

如果您只想确保每个常量的一个副本,甚至没有副本,那么您可以使用类模板,如下所示:

没有这样的运气。标准中无法保证模板使用了多少空间。所有模板都保证在as-if规则下只使用了一个潜在的许多副本 - 或者似乎在使用它们。事实上,它比这更糟糕,因为至少GCC 5.1.1实际上在我的系统上删除冗余static const unsigned int甚至-Os -flto。这意味着有两个翻译单元,unsigned int的初始化值可以在两个不同的位置找到,即使只使用其中一个(所有指针和引用仅指这个位置)。

答案 2 :(得分:0)

首先,在C ++ 98中不推荐在命名空间中使用static

  

D.2 static关键字
  在命名空间范围内声明对象时,不推荐使用static关键字(见3.3.5)

其次,const本身意味着C ++中的内部联系。

第三,确切的答案取决于您使用的编译器和选项。编译器/链接器可以消除重复项,尤其是在可以使用LTO(链路时间优化)的情况下。