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?
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 makes SOMETHING a constant pointer so you cannot write
SOMETHING = "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.
Related
I noticed C++ will not compile the following:
class No_Good {
static double const d = 1.0;
};
However it will happily allow a variation where the double is changed to an int, unsigned, or any integral type:
class Happy_Times {
static unsigned const u = 1;
};
My solution was to alter it to read:
class Now_Good {
static double d() { return 1.0; }
};
and figure that the compiler will be smart enough to inline where necessary... but it left me curious.
Why would the C++ designer(s) allow me to static const an int or unsigned, but not a double?
Edit: I am using visual studio 7.1 (.net 2003) on Windows XP.
Edit2:
Question has been answered, but for completion, the error I was seeing:
error C2864: 'd' : only const static integral data members can be initialized inside a class or struct
The problem is that with an integer, the compiler usually doesn't have to ever create a memory address for the constant. It doesn't exist at runtime, and every use of it gets inlined into the surrounding code. It can still decide to give it a memory location - if its address is ever taken (or if it's passed by const reference to a function), that it must. In order to give it an address, it needs to be defined in some translation unit. And in that case, you need to separate the declaration from the definition, since otherwise it would get defined in multiple translation units.
Using g++ with no optimization (-O0), it automatically inlines constant integer variables but not constant double values. At higher optimization levels (e.g. -O1), it inlines constant doubles. Thus, the following code compiles at -O1 but NOT at -O0:
// File a.h
class X
{
public:
static const double d = 1.0;
};
void foo(void);
// File a.cc
#include <stdio.h>
#include "a.h"
int main(void)
{
foo();
printf("%g\n", X::d);
return 0;
}
// File b.cc
#include <stdio.h>
#include "a.h"
void foo(void)
{
printf("foo: %g\n", X::d);
}
Command line:
g++ a.cc b.cc -O0 -o a # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a # Succeeds
For maximal portability, you should declare your constants in header files and define them once in some source file. With no optimization, this will not hurt performance, since you're not optimizing anyways, but with optimizations enabled, this can hurt performance, since the compiler can no longer inline those constants into other source files, unless you enable "whole program optimization".
I see no technical reason why
struct type {
static const double value = 3.14;
};
is forbidden. Any occasion you find where it works is due to non-portable implementation defined features. They also seem to be of only limited use. For integral constants initialized in class definitions, you can use them and pass them to templates as non-type arguments, and use them as the size of array dimensions. But you can't do so for floating point constants. Allowing floating point template parameters would bring its own set of rules not really worth the trouble.
Nonetheless, the next C++ version will allow that using constexpr:
struct type {
static constexpr double value = 3.14;
static constexpr double value_as_function() { return 3.14; }
};
And will make type::value a constant expression. In the meantime, your best bet is to follow the pattern also used by std::numeric_limits:
struct type {
static double value() { return 3.14; }
};
It will not return a constant expression (value is not known at compile time), but that only matters theoretical, since practical the value will be inlined anyway. See the constexpr proposal. It contains
4.4
Floating-point constant expressions
Traditionally, evaluation of
floating-point constant expression at
compile-time is a thorny issue. For
uniformity and generality, we suggest
to allow constant-expression data of
floating point types, initialized with
any floating-point constant
expressions. That will also increase
compatibility with C99 [ISO99, §6.6]
which allows
[#5] An expression that evaluates to a
constant is required in several
contexts. If a floating expression is
evaluated in the translation envi-
ronment, the arithmetic precision and
range shall be at least as great as if
the expression were being evaluated in
the execution environ- ment.
It doesn't really give a rationale, but here's what Stroustrup has to say about this in "The C++ Programming Language Third Edition":
10.4.6.2 Member Constants
It is also possible to initialize a
static integral constant member by
adding a constant-expression
initializer to its member declaration.
For example:
class Curious {
static const int c1 = 7; // ok, but remember definition
static int c2 = 11; // error: not const
const int c3 = 13; // error: not static
static const int c4 = f(17); // error: in-class initializer not constant
static const float c5 = 7.0; // error: in-class not integral
// ...
};
However, an initialized member must still be (uniquely) defined
somewhere, and the initializer may not
be repeated:
const int Curious::c1; // necessary, but don't repeat initializer here
I consider this a misfeature. When you need a symbolic constant
within a class declaration, use an
enumerator (4.8, 14.4.6, 15.3). For
example:
class X {
enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 };
// ...
};
In that way, no member definition is needed elsewhere, and you are not
tempted to declare variables,
floating-point numbers, etc.
And in Appendix C (Technicalities) in Section C.5 (Constant Expressions), Stroustrup has this to say about "constant expressions":
In places such as array bounds (5.2), case labels (6.3.2),
and initializers for enumerators (4.8), C++ requires a
constant expression. A constant expression evaluates to
an integral or enumeration constant. Such an expression
is composed of literals (4.3.1, 4.4.1, 4.5.1),
enumerators (4.8), and consts initialized by
constant expressions. In a template, an integer template
parameter can also be used (C.13.3). Floating literals (4.5.1)
can be used only if explicitly converted to an integral
type. Functions, class objects, pointers, and references
can be used as operands to the sizeof
operator (6.2) only.
Intuitively, constant expressions are simple expressions
that can be evaluated by the compiler before the program
is linked (9.1) and starts to run.
Note that he pretty much leaves out floating point as being able to play in 'constant expressions'. I suspect that floating point was left out of these types of constant expressions simply because they are not 'simple' enough.
I don't know why it would treat a double different from an int. I thought I had used that form before. Here's an alternate workaround:
class Now_Better
{
static double const d;
};
And in your .cpp file:
double const Now_Better::d = 1.0;
here is my understanding based on Stroustrup's statement about in-class definition
A class is typically declared in a header file and a header file is
typically included into many translation units. However, to avoid
complicated linker rules, C++ requires that every object has a unique
definition. That rule would be broken if C++ allowed in-class
definition of entities that needed to be stored in memory as objects.
http://www.stroustrup.com/bs_faq2.html#in-class
so basically, this is not allowed because C++ do not allow this. In order to make linker rules more simple, C++ requires that every object has a unique definition.
static member has only one instance in the class scope, not like regular static variables used heavily in C, which has only one instatnce inside one translation unit.
If static member is defined in class, and the class definition will be included into many translation unit, so that the linker has to do more work to decide which static member should be used as the only one through all the related translation unit.
But for regular static variables, they can only be used inside one translation unit, even in the case different static variables in different translation unit with same name, they will not affect each other. Linker can do simple work to link regular static variables inside one translation unit.
in order to decrease the complications and give the base function, C++ provide the only in-class definition for a static const of integral or enumeration type.
I noticed C++ will not compile the following:
class No_Good {
static double const d = 1.0;
};
However it will happily allow a variation where the double is changed to an int, unsigned, or any integral type:
class Happy_Times {
static unsigned const u = 1;
};
My solution was to alter it to read:
class Now_Good {
static double d() { return 1.0; }
};
and figure that the compiler will be smart enough to inline where necessary... but it left me curious.
Why would the C++ designer(s) allow me to static const an int or unsigned, but not a double?
Edit: I am using visual studio 7.1 (.net 2003) on Windows XP.
Edit2:
Question has been answered, but for completion, the error I was seeing:
error C2864: 'd' : only const static integral data members can be initialized inside a class or struct
The problem is that with an integer, the compiler usually doesn't have to ever create a memory address for the constant. It doesn't exist at runtime, and every use of it gets inlined into the surrounding code. It can still decide to give it a memory location - if its address is ever taken (or if it's passed by const reference to a function), that it must. In order to give it an address, it needs to be defined in some translation unit. And in that case, you need to separate the declaration from the definition, since otherwise it would get defined in multiple translation units.
Using g++ with no optimization (-O0), it automatically inlines constant integer variables but not constant double values. At higher optimization levels (e.g. -O1), it inlines constant doubles. Thus, the following code compiles at -O1 but NOT at -O0:
// File a.h
class X
{
public:
static const double d = 1.0;
};
void foo(void);
// File a.cc
#include <stdio.h>
#include "a.h"
int main(void)
{
foo();
printf("%g\n", X::d);
return 0;
}
// File b.cc
#include <stdio.h>
#include "a.h"
void foo(void)
{
printf("foo: %g\n", X::d);
}
Command line:
g++ a.cc b.cc -O0 -o a # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a # Succeeds
For maximal portability, you should declare your constants in header files and define them once in some source file. With no optimization, this will not hurt performance, since you're not optimizing anyways, but with optimizations enabled, this can hurt performance, since the compiler can no longer inline those constants into other source files, unless you enable "whole program optimization".
I see no technical reason why
struct type {
static const double value = 3.14;
};
is forbidden. Any occasion you find where it works is due to non-portable implementation defined features. They also seem to be of only limited use. For integral constants initialized in class definitions, you can use them and pass them to templates as non-type arguments, and use them as the size of array dimensions. But you can't do so for floating point constants. Allowing floating point template parameters would bring its own set of rules not really worth the trouble.
Nonetheless, the next C++ version will allow that using constexpr:
struct type {
static constexpr double value = 3.14;
static constexpr double value_as_function() { return 3.14; }
};
And will make type::value a constant expression. In the meantime, your best bet is to follow the pattern also used by std::numeric_limits:
struct type {
static double value() { return 3.14; }
};
It will not return a constant expression (value is not known at compile time), but that only matters theoretical, since practical the value will be inlined anyway. See the constexpr proposal. It contains
4.4
Floating-point constant expressions
Traditionally, evaluation of
floating-point constant expression at
compile-time is a thorny issue. For
uniformity and generality, we suggest
to allow constant-expression data of
floating point types, initialized with
any floating-point constant
expressions. That will also increase
compatibility with C99 [ISO99, §6.6]
which allows
[#5] An expression that evaluates to a
constant is required in several
contexts. If a floating expression is
evaluated in the translation envi-
ronment, the arithmetic precision and
range shall be at least as great as if
the expression were being evaluated in
the execution environ- ment.
It doesn't really give a rationale, but here's what Stroustrup has to say about this in "The C++ Programming Language Third Edition":
10.4.6.2 Member Constants
It is also possible to initialize a
static integral constant member by
adding a constant-expression
initializer to its member declaration.
For example:
class Curious {
static const int c1 = 7; // ok, but remember definition
static int c2 = 11; // error: not const
const int c3 = 13; // error: not static
static const int c4 = f(17); // error: in-class initializer not constant
static const float c5 = 7.0; // error: in-class not integral
// ...
};
However, an initialized member must still be (uniquely) defined
somewhere, and the initializer may not
be repeated:
const int Curious::c1; // necessary, but don't repeat initializer here
I consider this a misfeature. When you need a symbolic constant
within a class declaration, use an
enumerator (4.8, 14.4.6, 15.3). For
example:
class X {
enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 };
// ...
};
In that way, no member definition is needed elsewhere, and you are not
tempted to declare variables,
floating-point numbers, etc.
And in Appendix C (Technicalities) in Section C.5 (Constant Expressions), Stroustrup has this to say about "constant expressions":
In places such as array bounds (5.2), case labels (6.3.2),
and initializers for enumerators (4.8), C++ requires a
constant expression. A constant expression evaluates to
an integral or enumeration constant. Such an expression
is composed of literals (4.3.1, 4.4.1, 4.5.1),
enumerators (4.8), and consts initialized by
constant expressions. In a template, an integer template
parameter can also be used (C.13.3). Floating literals (4.5.1)
can be used only if explicitly converted to an integral
type. Functions, class objects, pointers, and references
can be used as operands to the sizeof
operator (6.2) only.
Intuitively, constant expressions are simple expressions
that can be evaluated by the compiler before the program
is linked (9.1) and starts to run.
Note that he pretty much leaves out floating point as being able to play in 'constant expressions'. I suspect that floating point was left out of these types of constant expressions simply because they are not 'simple' enough.
I don't know why it would treat a double different from an int. I thought I had used that form before. Here's an alternate workaround:
class Now_Better
{
static double const d;
};
And in your .cpp file:
double const Now_Better::d = 1.0;
here is my understanding based on Stroustrup's statement about in-class definition
A class is typically declared in a header file and a header file is
typically included into many translation units. However, to avoid
complicated linker rules, C++ requires that every object has a unique
definition. That rule would be broken if C++ allowed in-class
definition of entities that needed to be stored in memory as objects.
http://www.stroustrup.com/bs_faq2.html#in-class
so basically, this is not allowed because C++ do not allow this. In order to make linker rules more simple, C++ requires that every object has a unique definition.
static member has only one instance in the class scope, not like regular static variables used heavily in C, which has only one instatnce inside one translation unit.
If static member is defined in class, and the class definition will be included into many translation unit, so that the linker has to do more work to decide which static member should be used as the only one through all the related translation unit.
But for regular static variables, they can only be used inside one translation unit, even in the case different static variables in different translation unit with same name, they will not affect each other. Linker can do simple work to link regular static variables inside one translation unit.
in order to decrease the complications and give the base function, C++ provide the only in-class definition for a static const of integral or enumeration type.
This question already has answers here:
What is the difference between a definition and a declaration?
(27 answers)
Closed 7 years ago.
My question stems from studying Effective C++ by Scott Meyers.
In Item II of that book, following is written :
To limit the scope of a constant to a class, you must make it a member and, to ensure there's at most one copy of the constant, you must make it a static member.
That is correctly written. Then immediately the following example is given :
class GamePlayer {
private:
static const int NumTurns = 5;
int scores[NumTurns];
....
};
Then the following is written pertaining to the above example :
What you see above is a declaration and not a definition of NumTurns.
My First question is : What is the meaning of this statement ?
Immediately after that the following is mentioned :
Usually C++ requires that you provide a definition for anything you use, but class specific constants that are static and of integral type (e.g - integers, chars, bools) are an exception. As long as you don't take their address, you can declare them and use them without providing a definition. If you do take the address of a class constant, or if your compiler incorrectly insists on a definition even if you don't take the address, you provide a separate definition like this :
const int GamePlayer::Numturns; //definition of NumTurns
Why now it is a definition and not a declaration ?
I understand the difference in the context of a function but do not understand it in the context of a regular variable. Also, can someone expand on what the author means by
... if you do take the address of a class constant, or if your ..
part of the above quoted paragraph ?
P.S : I am a relatively newbie in C++.
Just as with functions, variables can have 'purely declarative' declarations, and actual definitions. You're confused because you probably didn't encounter many pure variable declarations before.
int i; // Definition
extern int i, j; // (Re)declares i, and declares j
extern int j = 0; // Defines j (confusing, eh?)
As you're used to with functions, definitions are declarations, but not all declarations are definitions. §3.1/2 reads
A declaration is a definition unless […] it declares a static data
member in a class definition (9.2, 9.4),
Thus in-class static data member declarations are never defining the variables they declare. However, sometimes, a variables definition doesn't have to be present. That is the case when you can use its value directly without necessitating the variables run-time existence.
In technical terms, as long as a static data member (or any entity, for that matter) is not 'odr-used', it doesn't have to be defined. Odr-use for all entities is defined in §3.2/3:
A variable x whose name appears as a potentially-evaluated expression
ex is odr-used by ex unless applying the lvalue-to-rvalue conversion
(4.1) to x yields a constant expression (5.20) that does not invoke
any non-trivial functions and, if x is an object, ex is an element of
the set of potential results of an expression e, where either the
lvalue-to-rvalue conversion (4.1) is applied to e, or e is a
discarded-value expression (Clause 5).
This looks complicated, and it was simpler in earlier versions of the standard. However, it roughly says that the variable is not odr-used by some expression when that expression "immediately" accesses the variables value, and this access yields a constant expression. Meyers example of "taking its address" is only one of many for odr-use.
For some class A and its static data member i,
class A {
static const int i = 57; // Declaration, not definition
};
const int A::i; // Definition in namespace scope. Not required per se.
It is stated here that:
Static const integral data members initialized in the class interface are not addressable variables. They are mere symbolic names for their associated values. Since they are not variables, it is not possible to determine their addresses. Note that this is not a compilation problem, but a linking problem. The static const variable that is initialized in the class interface does not exist as an addressable entity.
What "addressable entity" refers here is "instance" of static const data type. No instance, no address, i.e its only a declaration.
Note that static variables that are explicitly defined in a source file can be linked correctly, though.
class X
{
public:
static int const s_x = 34;
static int const s_y;
};
int const X::s_y = 12;
int main()
{
int const *ip = &X::s_x; // compiles, but fails to link
ip = &X::s_y; // compiles and links correctly
}
... if you do take the address of a class constant, or if your .. part of the above quoted paragraph ?
It means if such a member is odr-used, a definition at namespace scope is still required, but it should not have an initializer.
struct X {
const static int n = 1;
};
const int* p = &X::n; // X::n is odr-used
const int X::n; // ... so a definition is necessary
Why now it is a definition and not a declaration ?
Because this statement causes the compiler to generate an address for the static variable.
Also, can someone expand on what the author means by "if you do take
the address of a class constant":
When you make a pointer point to a variable you take its address.
A short answer is: a declaration says "this thing exists somewhere", and a definition causes the space to be allocated. In your case, you've declared it to be static and const. The compiler may be smart enough to notice that if you only ever use it as a value, it can simply replace that usage with the literal 5. It doesn't need to actually make space for a variable somewhere and fill it with the 5, it can use the 5 directly at compile-time. However, if you take the address of it, the compiler can no longer make that assumption and now needs to put that 5 into somewhere addressable. The compiler needs it to exist in one translation unit (roughly: one cpp file. See also the One Definition Rule.), so now you have to explicitly declare it somewhere.
struct A
{
int a = 5; //OK
const int b = 5; //OK
static const int c = 5; //OK
static int d = 5; //Error!
}
error: ISO C++ forbids in-class initialization of non-const static member 'A::d'
Why is it so? Can someone explain to me the reasoning behind this?
It has to do with where the data is stored. Here's a breakdown:
int: member variable, stored wherever the class instance is stored
const int: same as int
static const int: doesn't need to be stored, it can simply be "inlined" where used
static int: this must have a single storage location in the program...where?
Since the static int is mutable, it must be stored in an actual location somewhere, so that one part of the program can modify it and another part can see that modification. But it can't be stored in a class instance, so it must be more like a global variable. So why not just make it a global variable? Well, class declarations are usually in header files, and a header file may be #included in multiple translation units (.cpp files). So effectively the header file says "there is an int...somewhere." But the storage needs to be put into the corresponding .cpp file (like a global variable).
In the end, this is not really about initialization, but rather the storage. You could leave off the initializer and you'd still not have a valid program until you add this to your .cpp file:
int A::d; // initialize if you want to, default is zero
Without this, references to the static int will be undefined and linking will fail.
Initialization of static const member variables is available for integral and enum types. This feature existed in C++ since the first language standard (C++98). It is needed to facilitate usage of static const members in integral constant expressions (i.e. as compile-time constants), which is an important feature of the language. The reason integral and enum types were singled out and treated in this exceptional fashion is that integral constants are often used in compile-time contexts, which require no storage (no definition) for the constant.
The ability to supply initializers for non-static members is a new (for C++11) feature. It is a completely different feature, even though it looks similar at syntax level. Such initializers are used as construction-time initializers for those class members that were not explicitly initialized by the user.
In other words, it is not correct to lump these two features (initializers for static and non-static members) together. These two features are completely different. They are based on completely unrelated internal mechanics. Your question essentially applies the first feature: how come non-const static members cannot be initialized in-class? It is basically a C++98 question and the most likely answer to it is that there was never any reason to treat non-const static members in such an exceptional way. Non-const static members are treated in accordance with the general rules: they require a separate definition and the initializer should be provided at the point of definition.
If I were to do this
class Gone
{
public:
static const int a = 3;
}
it works but if do
class Gone
{
public:
static int a = 3;
}
it gives a compile error. Now I know why the second one doesn't work, I just don't know why the first one does.
Thanks in advance.
This trick works only for constant compile-time expressions. Consider the following simple example:
#include <iostream>
class Foo {
public:
static const int bar = 0;
};
int main()
{
std::cout << Foo::bar << endl;
}
It works just fine, because compiler knows that Foo::bar is 0 and never changes. Thus, it optimizes the whole thing away.
However, the whole thing breaks once you take the address of that variable like this:
int main()
{
std::cout << Foo::bar << " (" << &Foo::bar << ")" << std::endl;
}
Linker sends you to fix the program because compile-time constants don't have addresses.
Now, the second case in your example doesn't work simply because a non-constant variable cannot be a constant compile-time expression. Thus, you have to define it somewhere and cannot assign any values in initialization.
C++11, by the way, has constexpr. You can check Generalized constant expressions wiki (or C++11 standard :-)) for more info.
Also, be careful - with some toolchains you will never be able to link program as listed in your first example when optimizations are turned off, even if you never take an address of those variables. I think there is a BOOST_STATIC_CONSTANT macro in Boost to work around this problem (not sure if it works though because I reckon seeing linkage failures with some old gcc even with that macro).
The static const int declaration is legal because you're declaring a constant, not a variable. a doesn't exist as a variable - the compiler is free to optimize it out, replacing it with the declared value 3 anywhere a reference to Gone::a appears. C++ allows the static initialization in this restricted case where it's an integer constant.
You can find more details, including an ISO C++ standard citation here.
Initialization of variables has to be done at the point of definition, not the point of declaration in the general case. Inside the class brackets you only have a declaration and you need to provide a definition in a single translation unit*:
// can be in multiple translation units (i.e. a header included in different .cpp's)
struct test {
static int x; // declaration
static double d; // declaration
};
// in a single translation unit in your program (i.e. a single .cpp file)
int test::x = 5; // definition, can have initialization
double test::d = 5.0; // definition
That being said, there is an exception for static integral constants (and only integral constants) where you can provide the value of the constant in the declaration. The reason for the exception is that it can be used as a compile-time constant (i.e. to define the size of an array), and that is only possible if the compiler sees the value of the constant in all translation units where it is needed.
struct test {
static const int x = 5; // declaration with initialization
};
const int test::x; // definition, cannot have initialization
Going back to the original question:
Why is it not allowed for non-const integers?
because initialization happens in the definition and not declaration.
Why is it allowed for integral constants?
so that it can be used as a compile-time constant in all translation units
* The actual rules require the definition whenever the member attribute is used in the program. Now the definition of used is a bit tricky in C++03 as it might not be all that intuitive, for example the use of that constant as an rvalue does not constitute use according to the standard. In C++11 the term used has been replaced with odr-used in an attempt to avoid confusion.
A static const is defined in the class definition since everybody that uses the code need to know the value at compile time, not link time. An ordinary static is actually only declared in the class definition, but defined once, in one translation unit.
I seem to recall that originally (ARM) it was not allowed, and we used to use enum to define constants in class declarations.
The const case was explicitly introduced so as to support availability of the value in headers for use in constant expressions, such as array sizes.
I think (and please comment if I have this wrong) that strictly you still need to define the value:
const int Gone::a;
to comply with the One Definition Rule. However, in practice, you might find that the compiler optimises away the need for an address for Gone::a and you get away without it.
If you take:
const int* b = &Gone::a;
then you might find you do need the definition.
See the standard, $9.4.2:
ISO 1998:
"4 If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constantinitializer which shall be an integral constant expression
(5.19). 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."
Draft for c++11:
"3 If a static data member is of const effective literal type, its
declaration in the class definition can specify a constant-initializer
brace-or-equal-initializer with an initializer-clause that is an
integral constant expression. A static data member of effective
literal type can be declared in the class definition with the
constexpr specifier; if so, its declaration shall specify a
constant-initializer brace-or-equal-initializer with an
initializerclause that is an integral constant expression. In both
these cases, the member may appear in integral constant expressions.
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."
I am not sure entirely what this covers, but I think it means that we can now use the same idiom for floating point and possibly string literals.