简体   繁体   English

将const放在标题中

[英]Putting a const in a header

From everything I am reading and testing, there is no way (without a preprocessor macro) to define a constant in a shared header and ensure that each TU is not creating its own storage for that constant. 从我正在阅读和测试的所有内容中,没有办法(没有预处理器宏)在共享头中定义常量,并确保每个TU不为该常量创建自己的存储。

I can do this: 我可以做这个:

const int maxtt=888888;

Which is the same exactly as: 这完全相同:

static const int maxtt=888888;

And if this header is shared, it will work but each TU gets its own copy of maxtt . 如果此标头是共享的,它将起作用,但每个TU都有自己的maxtt副本。 I could also do this, to prevent that: 我也可以这样做,以防止:

extern const int maxtt;

But then I cannot define maxtt here; 但是我不能在这里定义maxtt ; that must be done in a CPP to avoid linker error. 必须在CPP中完成,以避免链接器错误。

Is my understanding correct? 我的理解是否正确?

Since the variable is constant, the fact that each TU gets its own copy is usually irrelevant. 由于变量是常量,因此每个TU获得自己的副本的事实通常是无关紧要的。

In C++, constants at namespace scope are implicitly static for this reason. 在C ++中,由于这个原因,命名空间范围内的常量是隐式static的。 Often this allows for better code than if you only had a single instance with external linkage, since (if the variable is actually a constant expression) the constant can often be folded right into the usage site and doesn't need to be stored at all. 通常这允许更好的代码,而不是只有一个具有外部链接的单个实例,因为(如果变量实际上是一个常量表达式),常量通常可以直接折叠到使用站点中,并且根本不需要存储。

So unless you really need to take the address of the constant, or something like that, you should stick with the static version. 因此,除非你真的需要获取常量的地址,或类似的东西,你应该坚持使用静态版本。 (And as you already observed, you can force external linkage by adding extern .) Another reason may be that you're initializing dynamically and only want one call to the initializer: (正如你已经看到,您可以通过添加强制外部链接extern 。)另一个原因可能是,你动态初始化和只想要一个调用初始化:

// header:
extern int const n;

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

The static construction ( int const n = init(); in the header) would cause the function to be called once in every TU. 静态构造(标头中的int const n = init();将导致在每个TU中调用一次函数。

You write, 你写,

“From everything I am reading and testing, there is no way (without a preprocessor macro) to define a constant in a shared header and ensure that each TU is not creating its own storage for that constant.” “从我正在阅读和测试的所有内容中,没有办法(没有预处理器宏)在共享头中定义一个常量,并确保每个TU不为该常量创建自己的存储。”

Happily that's incorrect . 幸运的是,这是不正确的

For a small integral value you can always just use an enum . 对于小的积分值,您始终只需使用enum The trade-off is that you can't pass the address of an enum value, because it has no address. 权衡是你不能传递enum值的地址,因为它没有地址。 It's pure value. 这是纯粹的价值。

However, saving space for an integral value is a pretty meaningless thing to do, since it's so small. 但是,为整数值节省空间是一件非常无意义的事情,因为它太小了。

So, let's consider a biggie thingy, 所以,让我们考虑一个大事,

struct BiggyThingy
{
    unsigned char zeroes[1000000];
    BiggyThingy(): zeroes() {}
};

Now how can we declare a BiggyThingy constant in a header file and ensure a single one overall for the whole program? 现在我们如何在头文件中声明一个BiggyThingy常量并确保整个程序的整体常量?

Using an inline function. 使用内联函数。

Well the simplest is this: 最简单的是:

inline BiggyThingy const& getBiggyThingy()
{
    static BiggyThingy const theThingy;
    return theThingy;
}

static BiggyThingy const& biggyThingy = getBiggyThingy();

If you don't want a reference taking up space (like a pointer) in each translation unit, then just use the function without the notation-simplifying reference. 如果您不希望每个转换单元中的引用占用空间(如指针),则只需使用不带符号简化引用的函数。

Using the template constant trick. 使用模板常量技巧。

Here is another way to provide the constant, leveraging a special rule for templates instead: 这是提供常量的另一种方法,而是利用模板的特殊规则:

template< class Dummy >
class BiggyThingyConstant_
{
public:
    static BiggyThingy const value;
};

template< class Dummy >
BiggyThingy const BiggyThingyConstant_<Dummy>::value;

typedef BiggyThingyConstant_<void> BiggyThingyConstant;

which can be accessed like 可以像访问一样访问

foo( BiggyThingyConstant::value )

Or if you want nicer notation you can add a reference per translation unit, as for the inline function solution. 或者,如果您需要更好的表示法,则可以为每个翻译单元添加一个引用,就像内联函数解决方案一样。

Disclaimer: 免责声明:

Code untouched by compiler. 代码未受编译器影响。

But you get the ideas, I think. 但我认为你会得到这些想法。 ;-) ;-)

This code generates a constant in the TU only if you apply any operation that requires the address of the constant. 仅当您应用任何需要常量地址的操作时,此代码才会在TU中生成常量。

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

This may work as well and avoids the linker problem (though it'll store maxtt in each TU if the address is requested): 这也可以工作并避免链接器问题(尽管如果请求地址, maxtt在每个TU中存储maxtt ):

constexpr int maxtt = 888888;

Avoid the extern construction as it can't be optimized. 避免extern结构,因为它无法优化。

If you're so worried about storage, use an enumeration: 如果您对存储非常担心,请使用枚举:

enum { maxtt = 888888 };

Enumerators are scalar rvalues and hence do not require storage. 枚举器是标量右值,因此不需要存储。 It is illegal to say &maxtt . &maxtt是违法的。

Indeed, your understanding of the semantics are correct. 实际上,您对语义的理解是正确的。

In practice, each translation unit might not get a copy of the storage for the integer. 实际上,每个翻译单元可能无法获得整数的存储副本。 One reason is that the compiler might implement the value as a literal wherever it is referenced. 一个原因是编译器可能会将值实现为文本,只要它被引用。 The linker might also be smart enough to discard the storage if it finds it isn't referenced. 链接器也可能足够聪明,如果发现它未被引用,则丢弃该存储。

The compiler might not be free to use a literal for your constant. 编译器可能无法为常量使用文字。 You might take a reference to that integer, or get a pointer to it. 您可以引用该整数,或获取指向它的指针。 In that case, you need the storage -- and you might even need the cross-compiland uniqueness. 在这种情况下,您需要存储 - 您甚至可能需要交叉compiland唯一性。 If you take the address of your const symbol in each compilation unit, you might find that its different since each object will get a unique, static copy. 如果在每个编译单元中获取const符号的地址,您可能会发现它的不同,因为每个对象都将获得唯一的静态副本。

You might have a similar problem if you use an enum; 如果使用枚举,可能会遇到类似的问题; your const int has storage, and you can take the address of that storage. 你的const int有存储空间,你可以获取该存储的地址。 You can't take the address of an enumerand until you store it someplace. 在将其存储到某个位置之前,您无法获取枚举的地址。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM