I have a class which is essentially just holds a bunch of constant definitions used through my application. For some reason though, long
s compile but float
s do not:
class MY_CONSTS
{
public :
static const long LONG_CONST = 1; // Compiles
static const float FLOAT_CONST = 0.001f; // C2864
};
Gives the following error:
1>c:\projects\myproject\Constant_definitions.h(71) : error C2864: 'MY_CONSTS::FLOAT_CONST' : only static const integral data members can be initialized within a class
Am I missing something?
To answer the actual question you asked: "because the standard says so".
Only variables of static, constant, integral types (including enumerations) may be initialized inside of a class declaration. If a compiler supports in-line initialization of floats, it is an extension. As others pointed out, the way to deal with static, constant, non-integral variables is to define and initialize them in the class's corresponding source file (not the header).
C++ Standard Section 9.2 "Class Members" item 4:
A member-declarator can contain a
constant-initializer only if it declares a static member (9.4) of
const integral or const enumeration
type, see 9.4.2.
Section 9.4.2 "Static Data Members" item 2:
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 namespace scope if it is
used in the program and the namespace
scope definition shall not contain an
initializer.
You should initialize them in the body of one of your cpp files:
class MY_CONSTS
{
public :
static const long LONG_CONST = 1; // Compiles
static const float FLOAT_CONST;
};
const float MY_CONSTS::FLOAT_CONST = 0.001f;
See Stroustrup's explanation. Relevant quote:
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. See D&E for an
explanation of C++'s design tradeoffs.
The rationale under the standard wording that others have given is the same for which template arguments can't be floating point number. To get consistent result you'll need the compiler implement the same evaluation as the one done at compile time, and that can be complicated for cross-compiler and in the case where the program plays with rounding mode.
From memory, in C++0X, the notion of constant expression has been extended and so your code would be valid (but it is unspecified in the result of floating point constant expressions are the same when evaluated at run-time or at compile time).
what about:
class MY_CONSTS
{
public :
static const long LONG_CONST;
static const float FLOAT_CONST;
};
const long MY_CONSTS::LONG_CONST = 1;
const float MY_CONSTS::FLOAT_CONST = 0.001f;
(though, i cannot give any explanation of this specific case...)
From 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 (5.19).
In that case, the member can 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.
And 5.19/1:
In several places, C + + requires
expressions that evaluate to an
integral or enumeration constant: as
array bounds (8.3.4, 5.3.4), as case
expressions (6.4.2), as bit-field
lengths (9.6), as enumerator
initializers (7.2), as static member
initializers (9.4.2), and as integral
or enumeration non-type template
arguments (14.3). constant-expression:
conditional-expression An integral
constant-expression can involve only
literals (2.13), enumerators, const
variables or static data members of
integral or enumeration types
initialized with constant expressions
(8.5), non-type template parameters of
integral or enumeration types, and
sizeof expressions. Floating
literals (2.13.3) can appear only if
they are cast to integral or
enumeration types. Only type
conversions to integral or enumeration
types can be used. In particular,
except in sizeof expressions,
functions, class objects, pointers, or
references shall not