C++ 将常量放在标题中

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都有自己

从我正在阅读和测试的所有内容来看,没有办法(没有预处理器宏)在共享头中定义一个常量,并确保每个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 )