How to declare a static const char* in your header file?
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 err开发者_开发知识库or:
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?
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:
private:
static const char *SOMETHING;
static const int MyInt = 8; // would be ok
In the .cpp file:
const char *YourClass::SOMETHING = "something";
C++ standard, 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.
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.
With C++11 you can use the constexpr
keyword and write in your header:
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;
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
class Foo {
static const char *SOMETHING;
// rest of class
};
// cpp file
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.
class A{
public:
static const char* SOMETHING() { return "something"; }
};
I do it all the time - especially for expensive const default parameters.
class A{
static
const expensive_to_construct&
default_expensive_to_construct(){
static const expensive_to_construct xp2c(whatever is needed);
return xp2c;
}
};
If you're using Visual C++, you can non-portably do this using hints to the linker...
// 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.
Keep in mind this will only work with the Microsoft toolchain. Don't expect this to be portable.
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)
#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)
#include <iostream>
#include "values.hpp"
int main()
{
std::cout << "from main: " << Values::myValue << std::endl;
std::cout << "from other: " << otherCompUnit() << std::endl;
}
(other.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:
from main: This is a value
from other: This is a value
In MSVC, you may instead be able to use __declspec(selectany)
For example:
__declspec(selectany) const char* data = "My data";
Constant initializer allowed by C++ Standard only for integral or enumeration types. See 9.4.2/4 for details:
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.
And 9.4.2/7:
Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).
So you should write somewhere in cpp file:
const char* SomeClass::SOMETHING = "sommething";
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.
精彩评论