C++ 将常量放在标题中
从我正在阅读和测试的所有内容来看,没有办法(没有预处理器宏)在共享头中定义一个常量,并确保每个TU没有为该常量创建自己的存储C++ 将常量放在标题中,c++,C++,从我正在阅读和测试的所有内容来看,没有办法(没有预处理器宏)在共享头中定义一个常量,并确保每个TU没有为该常量创建自己的存储 static int maxtt = 888888; int * pmaxtt = &maxtt; //address of constant requested. 我可以这样做: const int maxtt=8888888 与以下内容完全相同: static const int maxtt=8888888 如果这个头是共享的,它会工作,但是每个TU都有自己
static int maxtt = 888888;
int * pmaxtt = &maxtt; //address of constant requested.
我可以这样做:
const int maxtt=8888888代码>
与以下内容完全相同:
static const int maxtt=8888888代码>
如果这个头是共享的,它会工作,但是每个TU都有自己的maxtt
副本。我也可以这样做,以防止:
extern const int maxtt代码>
但是我不能在这里定义maxtt
;这必须在CPP中完成,以避免链接器错误
我的理解正确吗?只有在应用任何需要常量地址的操作时,此代码才会在TU中生成常量
static int maxtt = 888888;
int * pmaxtt = &maxtt; //address of constant requested.
这也可以工作,避免了链接器问题(尽管如果请求地址,它将在每个TU中存储maxtt
):
避免外部构造,因为它无法优化。由于变量是常量,每个TU都有自己的副本这一事实通常是不相关的
<>在C++中,命名空间范围中的常数是隐式的<代码>静态< /代码>。这通常比只有一个外部链接的实例更好,因为(如果变量实际上是一个常量表达式)常量通常可以直接折叠到使用站点中,并且根本不需要存储
因此,除非您真的需要获取常量的地址,或者类似的东西,否则您应该坚持使用静态版本。(正如您已经观察到的,您可以通过添加extern
强制外部链接)另一个原因可能是您正在动态初始化,并且只需要对初始值设定项进行一次调用:
// header:
extern int const n;
// one implementation:
int const n = init_function_with_side_effects();
静态构造(int const n=init();
在标题中)会导致函数在每个TU中调用一次。实际上,您对语义的理解是正确的
实际上,每个转换单元可能无法获得整数存储的副本。一个原因是编译器可能在引用该值的任何地方将其作为文本实现。链接器也可能足够聪明,如果发现存储未被引用,就丢弃它
编译器可能无法自由地为常量使用文本。您可以引用该整数,或者获取指向该整数的指针。在这种情况下,您需要存储—甚至可能需要交叉编译和唯一性。如果在每个编译单元中获取const
符号的地址,您可能会发现其不同,因为每个对象将获得唯一的静态副本
如果使用枚举,您可能会遇到类似的问题;您的const int
具有存储器,您可以获取该存储器的地址。在将枚举器存储到某个地方之前,您无法获取其地址。如果您非常担心存储,请使用枚举:
enum { maxtt = 888888 };
枚举数是标量右值,因此不需要存储。说你写的&maxtt
是违法的
“从我正在阅读和测试的所有内容来看,在没有预处理器宏的情况下,无法在共享标头中定义常量并确保每个TU没有为该常量创建自己的存储。”
幸好这是不正确的
对于较小的整数值,始终可以使用enum
。取舍是不能传递enum
值的地址,因为它没有地址。这是纯粹的价值
然而,为整数值节省空间是一件毫无意义的事情,因为它太小了
所以,让我们考虑一个大问题,
struct BiggyThingy
{
unsigned char zeroes[1000000];
BiggyThingy(): zeroes() {}
};
现在,我们如何在头文件中声明一个BiggyThingy
常量,并确保整个程序都有一个常量
使用内联函数。
最简单的是:
inline BiggyThingy const& getBiggyThingy()
{
static BiggyThingy const theThingy;
return theThingy;
}
static BiggyThingy const& biggyThingy = getBiggyThingy();
如果您不希望引用在每个翻译单元中占用空间(如指针),那么只需使用不带简化引用符号的函数即可
使用模板常量技巧。
以下是提供常量的另一种方法,它利用了模板的特殊规则:
template< class Dummy >
class BiggyThingyConstant_
{
public:
static BiggyThingy const value;
};
template< class Dummy >
BiggyThingy const BiggyThingyConstant_<Dummy>::value;
typedef BiggyThingyConstant_<void> BiggyThingyConstant;
或者,如果您想要更好的表示法,您可以为每个翻译单元添加一个引用,就像内联函数解决方案一样
免责声明:
编译器未触及的代码
但我想,你会明白的 你为什么要阻止它?无论如何,它可能会被优化为888888
,所以不要认为它会增加代码大小。谢谢你们两位,好的方面。为了完整起见,让我注意一下,您的理解是错误的:在头文件中提供常量的三种方法,最坏情况下,存储是在一个位置分配的,在中讨论过,目前被选为“解决方案”,讨论了为什么您可能需要这些技术,但奇怪的是,它甚至没有提到这些技术。除了我的新答案外,其他答案主要是误导性的,给人一种错误的印象,即你对这件事的看法是正确的。我不确定我是否理解这个问题。使常量值可供编译器使用的整个要点是,它的存储将分配给使用它的地方(通常在指令中),而不是其他地方。现在它确实回答了问题:)+1我缺少关于用于此的工具链的信息(至少是编译器+链接器)。我不确定C++标准是否会对它进行任何超越编译的结果,所以结果可能会很大程度上取决于工具的使用。呃,OP的“没有办法(没有预处理器宏)在共享的头中定义常数,并确保每个TU不为那个常数创建它自己的存储空间”是非常不正确的。所以我不认为他对语义学的理解是正确的。然而,他对简单的const
声明的理解似乎大致正确。
foo( BiggyThingyConstant::value )