[英]Overhead of static constants when included into many translation units?
在头文件中,可以在一行中声明和(预)定义全局常量。
// 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个副本? 还是只复制指针和值?
如果有开销,是否有一种方法可以避免它而无需分开声明和定义?
(我也猜想在这种用法中“静态”是多余的,但无论如何我都喜欢把它放在那里)
字符串文字是否在各种翻译单元中重复是实现质量的问题。
直接声明的对象将在包含此标头的每个翻译单元中重复。 虽然不多。 在不直接或间接使用某些常量地址的转换单元中,可以对其进行优化。
如果要确保每个常量只有一个副本,甚至没有副本,则可以使用类模板,如下所示:
#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
函数,每个常数一个。
如果我将其包含在100个单位中,我的程序是否将每个字符串和数组文字包含100个副本? 还是只复制指针和值?
该标准不承诺合并字符串文字,因此取决于实现。 在GNU / Linux上使用GCC 5.1.1进行的简单测试表明,字符串文字不是在未优化的构建中合并的,而是在使用-O
或-Os
时合并的。 但这只是合并实际的char
数组。 在某种程度上,编译器没有优化指针或数字常量的存储(如果它们是转换单元的本地存储,不是ODR使用的,并且是POD类型的,则它们显然是在以下情况下消除的候选对象)但是,编译器可能无法轻松合并它们。 该标准要求它们是不同的对象,因此必须具有不同的地址。 即使您要获取其地址,按原样的规则仍可能允许将其删除,但这通常需要实现可能不支持的全局程序优化或链接时优化(包括库),实现可能不支持,仅以有限的方式支持和/或仅取决于编译器和链接器设置。 换句话说,它只会在适当的情况下发生,但更有可能不会发生。
我自己的测试表明,即使使用-Os -flto
(针对大小进行优化并启用链接时优化),GCC 5.1.1也不合并由const
ref公开的static const unsigned int
对象。 坦率地说,如果任何当代的实现都执行了这一困难而晦涩的优化,我将感到惊讶。
(我也猜想在这种用法中“静态”是多余的,但无论如何我都喜欢把它放在那里)
如果有多个翻译单元,这不是多余的,因为否则会违反一个定义规则(ODR)。 但是,作为旁注,很长时间以来,在命名空间范围内, static
语法已被认为过时了(考虑使用在C ++ 98中引入的匿名命名空间)。
(响应干杯和hth。-Alf)
如果要确保每个常量只有一个副本,甚至没有副本,则可以使用类模板,如下所示:
没有这种运气。 标准中不能保证模板使用了多少空间。 模板所做的全部保证是,按照潜在规则,仅使用了或似乎要使用的潜在多个副本中的一个。 实际上,这比那更糟,因为至少GCC 5.1.1实际上即使在系统上使用-Os -flto
也不会删除冗余的static const unsigned int
。 这意味着使用两个转换单元,即使在仅使用其中一个的情况下,也可以在两个单独的位置找到unsigned int
的初始化程序值(所有指针和引用仅引用此位置)。
首先,自C ++ 98起,不建议在名称空间中使用static
:
D.2
static
关键字
在命名空间范围内声明对象时,不建议使用static
关键字(请参阅3.3.5)
其次, const
暗示C ++本身具有内部链接。
第三,确切答案取决于您使用的编译器和选项。 可以通过编译器/链接器消除重复,特别是如果可以使用LTO(链接时间优化)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.