将const放在标题中

时间:2013-01-20 22:56:21

标签: c++

从我正在阅读和测试的所有内容中,没有办法(没有预处理器宏)在共享头中定义常量,并确保每个TU不为该常量创建自己的存储。

我可以这样做:

const int maxtt=888888;

与...完全相同:

static const int maxtt=888888;

如果此标头是共享的,它将起作用,但每个TU都有自己的maxtt副本。我也可以这样做,以防止:

extern const int maxtt;

但是我不能在这里定义maxtt;必须在CPP中完成,以避免链接器错误。

我的理解是否正确?

5 个答案:

答案 0 :(得分:17)

由于变量是常量,因此每个TU获得自己的副本这一事实通常是无关紧要的。

在C ++中,由于这个原因,命名空间范围内的常量隐式static。通常,这允许更好的代码,而不是只有一个具有外部链接的实例,因为(如果变量实际上是一个常量表达式),常量通常可以折叠到使用站点中并且不会根本不需要存储。

因此,除非你真的需要获取常量的地址或类似的东西,否则你应该坚持使用静态版本。 (正如您已经观察到的,您可以通过添加extern来强制外部链接。)另一个原因可能是您正在动态初始化并且只需要一次调用初始化程序:

// header:
extern int const n;

// one implementation:
int const n = init_function_with_side_effects();

静态构造(标题中的int const n = init();)将导致在每个TU中调用一次函数。

答案 1 :(得分:9)

你写,

  

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

可以像

一样访问
foo( BiggyThingyConstant::value )

或者如果你想要更好的表示法,你可以为每个翻译单元添加一个引用,就像内联函数解决方案一样。

声明:

编译器未触及的代码。

但我认为你得到了这些想法。 ; - )

答案 2 :(得分:6)

只有在应用任何需要常量地址的操作时,此代码才会在TU中生成常量。

static int maxtt = 888888;
int * pmaxtt = &maxtt; //address of constant requested.

这也可以起作用并避免链接器问题(尽管如果请求地址,它会在每个TU中存储maxtt):

constexpr int maxtt = 888888;

避免extern构造,因为它无法优化。

答案 3 :(得分:4)

如果您对存储非常担心,请使用枚举:

enum { maxtt = 888888 };

枚举器是标量右值,因此不需要存储。说&maxtt是违法的。

答案 4 :(得分:0)

事实上,您对语义的理解是正确的。

实际上,每个翻译单元可能无法获得整数的存储副本。一个原因是编译器可能会将值实现为文本,只要它被引用。链接器也可能足够智能,如果发现存储未被引用,则丢弃该存储。

编译器可能无法为常量使用文字。您可以引用该整数,或获取指向它的指针。在这种情况下,您需要存储 - 您甚至可能需要交叉compiland唯一性。如果您在每个编译单元中获取const符号的地址,您可能会发现它的不同,因为每个对象都将获得唯一的静态副本。

如果使用枚举,则可能会遇到类似的问题;您的const int有存储空间,您可以获取该存储空间的地址。在将它存储到某个地方之前,你不能获取enumerand的地址。