如何在头文件中声明 static const char* ?
我想在我的头文件中定义一个常量 char* 供我的 .cpp 文件使用。所以我尝试了这个:
private:
static const char *SOMETHING = "sommething";
这给我带来了以下编译器错误:
错误 C2864:“SomeClass::SOMETHING”: 仅静态常量整型数据 成员可以在一个 类
我是 C++ 新手。这是怎么回事?为什么这是非法的?或者你可以怎样做呢?
I'd like to define a constant char* in my header file for my .cpp file to use. So I've tried this:
private:
static const char *SOMETHING = "sommething";
Which brings me with the following compiler error:
error C2864: 'SomeClass::SOMETHING' :
only static const integral data
members can be initialized within a
class
I'm new to C++. What is going on here? Why is this illegal? And how can you do it alternatively?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
注意:自 C++11 以来,这已经发生了变化,也请阅读其他答案
您需要在翻译单元中定义静态变量,除非它们是整型。
在您的标头中:
在 .cpp 文件中:
C++ 标准,9.4.2/4:
NB : this has changed since C++11, read other answers too
You need to define static variables in a translation unit, unless they are of integral types.
In your header:
In the .cpp file:
C++ standard, 9.4.2/4:
使用 C++11,您可以使用
constexpr
关键字并在标头中写入:注释:
constexpr
使SOMETHING
成为常量指针,因此您无法编写稍后。
根据您的编译器,您可能还需要在 .cpp 文件中编写显式定义:
With C++11 you can use the
constexpr
keyword and write in your header:Notes:
constexpr
makesSOMETHING
a constant pointer so you cannot writelater on.
Depending on your compiler, you might also need to write an explicit definition in the .cpp file:
回答OP关于为什么只允许使用整数类型的问题。
当一个对象用作左值(即作为在存储中具有地址的对象)时,它必须满足“一个定义规则”(ODR),即它必须在一个且仅一个翻译单元中定义。编译器不能也不会决定在哪个翻译单元中定义该对象。这是您的责任。通过在某处定义该对象,您不仅仅是在定义它,您实际上是在告诉编译器您想要在这里定义它,在这个特定的地方翻译单位。
同时,在C++语言中整型常量具有特殊的地位。它们可以形成积分常量表达式(ICE)。在 ICE 中,整型常量被用作普通值,而不是对象(即,此类整型值在存储中是否有地址并不相关)。事实上,ICE 是在编译时评估的。为了促进积分常量的使用,它们的值必须是全局可见的。并且常量本身并不需要在存储中占据实际位置。由于这种整型常量受到特殊待遇:允许在头文件中包含它们的初始化程序,并且放宽了提供定义的要求(首先是事实上的,然后是法律上的)。
其他常量类型没有这样的属性。其他常量类型实际上总是用作左值(或者至少不能参与 ICE 或类似于 ICE 的任何内容),这意味着它们需要定义。其余的如下。
To answer the OP's question about why it is only allowed with integral types.
When an object is used as an lvalue (i.e. as something that has address in storage), it has to satisfy the "one definition rule" (ODR), i.e it has to be defined in one and only one translation unit. The compiler cannot and will not decide which translation unit to define that object in. This is your responsibility. By defining that object somewhere you are not just defining it, you are actually telling the compiler that you want to define it here, in this specific translation unit.
Meanwhile, in C++ language integral constants have special status. They can form integral constant expressions (ICEs). In ICEs integral constants are used as ordinary values, not as objects (i.e. it is not relevant whether such integral value has address in the storage or not). In fact, ICEs are evaluated at compile time. In order to facilitate such a use of integral constants their values have to be visible globally. And the constant itself don't really need an actual place in the storage. Because of this integral constants received special treatment: it was allowed to include their initializers in the header file, and the requirement to provide a definition was relaxed (first de facto, then de jure).
Other constant types has no such properties. Other constant types are virtually always used as lvalues (or at least can't participate in ICEs or anything similar to ICE), meaning that they require a definition. The rest follows.
错误是您无法在类中初始化
static const char*
。您只能在那里初始化整数变量。你需要在类中声明成员变量,然后在类外对其进行初始化:
// 头文件
// cpp 文件
如果这看起来很烦人,可以认为这是因为初始化只能出现在一个翻译单元中。如果它在类定义中,通常会包含在多个文件中。常量整数是一种特殊情况(这意味着错误消息可能并不那么清晰),编译器可以有效地用整数值替换变量的使用。
相比之下,
char*
变量指向内存中的实际对象,该对象需要真正存在,并且是定义(包括初始化)使该对象存在。 “一个定义规则”意味着您不想将其放入标头中,因为这样包括该标头的所有翻译单元都将包含该定义。它们无法链接在一起,即使字符串中包含相同的字符,因为根据当前的 C++ 规则,您定义了两个具有相同名称的不同对象,这是不合法的。事实上,它们碰巧具有相同的字符并不意味着它是合法的。The error is that you cannot initialize a
static const char*
within the class. You can only initialize integer variables there.You need to declare the member variable in the class, and then initialize it outside the class:
// header file
// cpp file
If this seems annoying, think of it as being because the initialization can only appear in one translation unit. If it was in the class definition, that would usually be included by multiple files. Constant integers are a special case (which means the error message perhaps isn't as clear as it might be), and compilers can effectively replace uses of the variable with the integer value.
In contrast, a
char*
variable points to an actual object in memory, which is required to really exist, and it's the definition (including initialization) which makes the object exist. The "one definition rule" means you therefore don't want to put it in a header, because then all translation units including that header would contain the definition. They could not be linked together, even though the string contains the same characters in both, because under current C++ rules you've defined two different objects with the same name, and that's not legal. The fact that they happen to have the same characters in them doesn't make it legal.我一直这样做——特别是对于昂贵的 const 默认参数。
I do it all the time - especially for expensive const default parameters.
如果您使用的是 Visual C++,则可以使用链接器的提示以不可移植的方式执行此操作...
__declspec(selectany)
意味着即使Foo::Bar
将get 在多个目标文件中声明,链接器只会选取一个。请记住,这仅适用于 Microsoft 工具链。不要指望它是便携式的。
If you're using Visual C++, you can non-portably do this using hints to the linker...
__declspec(selectany)
means that even thoughFoo::Bar
will get declared in multiple object files, the linker will only pick up one.Keep in mind this will only work with the Microsoft toolchain. Don't expect this to be portable.
有一个技巧可以与模板一起使用来提供仅 H 文件常量。
(注意,这是一个丑陋的例子,但至少在 g++ 4.6.1 中逐字工作。)
(values.hpp 文件)
(main.cpp)
(other.cpp)
编译(例如 g++ -o main main.cpp other.) cpp && ./main) 并看到两个编译单元引用标头中声明的相同常量:
在 MSVC 中,您可以使用 __declspec(selectany)
例如:
There is a trick you can use with templates to provide H file only constants.
(note, this is an ugly example, but works verbatim in at least in g++ 4.6.1.)
(values.hpp file)
(main.cpp)
(other.cpp)
Compile (e.g. g++ -o main main.cpp other.cpp && ./main) and see two compilation units referencing the same constant declared in a header:
In MSVC, you may instead be able to use __declspec(selectany)
For example:
C++ 标准仅允许对整型或枚举类型使用常量初始值设定项。详情参见9.4.2/4:
和 9.4.2/7:
所以你应该在cpp文件中的某处写入:
Constant initializer allowed by C++ Standard only for integral or enumeration types. See 9.4.2/4 for details:
And 9.4.2/7:
So you should write somewhere in cpp file:
为了回答为什么这个问题,整型类型很特殊,因为它们不是对已分配对象的引用,而是重复和复制的值。这只是定义语言时做出的实现决策,即以尽可能高效和“内联”的方式处理对象系统外部的值。
这并不能完全解释为什么它们被允许作为类型中的初始值设定项,但可以将其视为本质上的
#define
,然后它将作为类型的一部分而不是对象的一部分有意义。To answer the why question, integral types are special in that they are not a reference to an allocated object but rather values that are duplicated and copied. It's just an implementation decision made when the language was defined, which was to handle values outside the object system and in as efficient and "inline" a fashion as possible.
This doesn't exactly explain why they are allowed as initializors in a type, but think of it as essentially a
#define
and then it will make sense as part of the type and not part of the object.