C++ 如何在头文件中声明静态 const char*?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1639154/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to declare a static const char* in your header file?
提问by Mark
I'd like to define a constant char* in my header file for my .cpp file to use. So I've tried this:
我想在我的头文件中定义一个常量 char* 供我的 .cpp 文件使用。所以我试过这个:
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
错误 C2864:“SomeClass::SOMETHING”:只能在类中初始化静态常量整数数据成员
I'm new to C++. What is going on here? Why is this illegal? And how can you do it alternatively?
我是 C++ 的新手。这里发生了什么?为什么这是非法的?你怎么能做到这一点呢?
回答by KeatsPeeks
You need to define static variables in a translation unit, unless they are of integral types.
您需要在翻译单元中定义静态变量,除非它们是整数类型。
In your header:
在您的标题中:
private:
static const char *SOMETHING;
static const int MyInt = 8; // would be ok
In the .cpp file:
在 .cpp 文件中:
const char *YourClass::SOMETHING = "something";
C++ standard, 9.4.2/4:
C++ 标准,9.4.2/4:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression. In that case, the member can appear in integral constant expressions within its scope. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
如果静态数据成员是 const 整型或 const 枚举类型,则它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式。在这种情况下,该成员可以出现在其范围内的整数常量表达式中。如果在程序中使用该成员,则该成员仍应在名称空间范围内定义,并且名称空间范围定义不应包含初始化程序。
回答by AnT
To answer the OP's question about why it is only allowed with integral types.
回答 OP 关于为什么只允许使用整数类型的问题。
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 definingthat object somewhere you are not just defining it, you are actually telling the compiler that you want to define it here, in thisspecific translation unit.
当一个对象用作左值(即作为具有存储地址的东西)时,它必须满足“一个定义规则”(ODR),即它必须在一个且仅一个翻译单元中定义。编译器不能也不会决定在哪个翻译单元中定义该对象。这是您的责任。通过定义你不只是它定义该对象的地方,你实际上是在告诉编译器要确定它在这里,在这个特定的翻译单元。
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).
同时,整数常量在C++语言中有着特殊的地位。它们可以形成积分常数表达式 (ICE)。在 ICE 中,整数常量用作普通值,而不是对象(即,此类整数值是否在存储中具有地址无关紧要)。事实上,ICE 是在编译时评估的。为了促进积分常数的这种使用,它们的值必须全局可见。并且常量本身并不真正需要在存储中的实际位置。由于这个整数常量得到了特殊处理:允许在头文件中包含它们的初始化器,并且放宽了提供定义的要求(首先是事实上的,然后是法律上的)。
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.
其他常量类型没有这样的属性。其他常量类型实际上总是用作左值(或者至少不能参与 ICE 或任何类似于 ICE 的东西),这意味着它们需要定义。其余的如下。
回答by Ignitor
With C++11 you can use the constexpr
keyword and write in your header:
使用 C++11,您可以使用constexpr
关键字并在标题中写入:
private:
static constexpr const char* SOMETHING = "something";
Notes:
笔记:
constexpr
makesSOMETHING
a constant pointer so you cannot writeSOMETHING = "something different";
later on.
Depending on your compiler, you might also need to write an explicit definition in the .cpp file:
constexpr const char* MyClass::SOMETHING;
constexpr
制作SOMETHING
一个常量指针,所以你不能写SOMETHING = "something different";
稍后的。
根据您的编译器,您可能还需要在 .cpp 文件中编写显式定义:
constexpr const char* MyClass::SOMETHING;
回答by Steve Jessop
The error is that you cannot initialize a static const char*
within the class. You can only initialize integer variables there.
错误是您无法static const char*
在类中初始化 a 。您只能在那里初始化整数变量。
You need to declare the member variable in the class, and then initialize it outside the class:
需要在类中声明成员变量,然后在类外初始化:
// header file
// 头文件
class Foo {
static const char *SOMETHING;
// rest of class
};
// cpp file
//.cpp文件
const char *Foo::SOMETHING = "sommething";
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.
相比之下,char*
变量指向内存中的实际对象,它需要真正存在,而定义(包括初始化)使对象存在。“一个定义规则”意味着您因此不想将它放在标题中,因为这样所有包括该标题在内的翻译单元都将包含该定义。它们无法链接在一起,即使字符串在两者中都包含相同的字符,因为在当前的 C++ 规则下,您已经定义了两个具有相同名称的不同对象,这是不合法的。它们碰巧具有相同的字符这一事实并不使其合法。
回答by pgast
class A{
public:
static const char* SOMETHING() { return "something"; }
};
I do it all the time - especially for expensive const default parameters.
我一直这样做 - 特别是对于昂贵的 const 默认参数。
class A{
static
const expensive_to_construct&
default_expensive_to_construct(){
static const expensive_to_construct xp2c(whatever is needed);
return xp2c;
}
};
回答by asveikau
If you're using Visual C++, you can non-portably do this using hints to the linker...
如果您使用的是 Visual C++,则可以使用链接器的提示以不可移植的方式执行此操作...
// In foo.h...
class Foo
{
public:
static const char *Bar;
};
// Still in foo.h; doesn't need to be in a .cpp file...
__declspec(selectany)
const char *Foo::Bar = "Blah";
__declspec(selectany)
means that even though Foo::Bar
will get declared in multiple object files, the linker will only pick up one.
__declspec(selectany)
意味着即使Foo::Bar
将在多个目标文件中声明,链接器也只会选择一个。
Keep in mind this will only work with the Microsoft toolchain. Don't expect this to be portable.
请记住,这仅适用于 Microsoft 工具链。不要指望它是可移植的。
回答by Torlack
There is a trick you can use with templates to provide H file only constants.
有一个技巧可以与模板一起使用以提供仅 H 文件的常量。
(note, this is an ugly example, but works verbatim in at least in g++ 4.6.1.)
(注意,这是一个丑陋的例子,但至少在 g++ 4.6.1 中可以逐字运行。)
(values.hpp file)
(values.hpp 文件)
#include <string>
template<int dummy>
class tValues
{
public:
static const char* myValue;
};
template <int dummy> const char* tValues<dummy>::myValue = "This is a value";
typedef tValues<0> Values;
std::string otherCompUnit(); // test from other compilation unit
(main.cpp)
(主.cpp)
#include <iostream>
#include "values.hpp"
int main()
{
std::cout << "from main: " << Values::myValue << std::endl;
std::cout << "from other: " << otherCompUnit() << std::endl;
}
(other.cpp)
(其他.cpp)
#include "values.hpp"
std::string otherCompUnit () {
return std::string(Values::myValue);
}
Compile (e.g. g++ -o main main.cpp other.cpp && ./main) and see two compilation units referencing the same constant declared in a header:
编译(例如 g++ -o main main.cpp other.cpp && ./main)并看到两个编译单元引用在头文件中声明的相同常量:
from main: This is a value
from other: This is a value
In MSVC, you may instead be able to use __declspec(selectany)
在 MSVC 中,您可以改为使用 __declspec(selectany)
For example:
例如:
__declspec(selectany) const char* data = "My data";
回答by Kirill V. Lyadvinsky
Constant initializer allowed by C++ Standard only for integral or enumeration types. See 9.4.2/4 for details:
C++ 标准仅允许用于整数或枚举类型的常量初始值设定项。详见9.4.2/4:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a name- space scope if it is used in the program and the namespace scope definition shall not contain an initializer.
如果静态数据成员是 const 整型或 const 枚举类型,则它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式 (5.19)。在这种情况下,成员可以出现在整数常量表达式中。如果在程序中使用该成员,则该成员仍应在名称空间范围内定义,并且名称空间范围定义不应包含初始值设定项。
And 9.4.2/7:
和 9.4.2/7:
Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).
静态数据成员的初始化和销毁与非本地对象(3.6.2、3.6.3)完全一样。
So you should write somewhere in cpp file:
所以你应该在 cpp 文件的某个地方写:
const char* SomeClass::SOMETHING = "sommething";
回答by DigitalRoss
To answer the whyquestion, 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.
这并不能完全解释为什么允许它们作为类型中的初始值设定项,而是将其视为本质上的 a #define
,然后它将作为类型的一部分而不是对象的一部分有意义。