tags:

views:

661

answers:

9

I have a class which is essentially just holds a bunch of constant definitions used through my application. For some reason though, longs compile but floats 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?

A: 

compiles fine with g++ (gcc c++ compiler) so maybe it's a problem with the compiler you are using.

$ cat 1.cpp

class MY_CONSTS
{
public :
    static const long   LONG_CONST = 1;      // Compiles
    static const float FLOAT_CONST = 0.001f; // C2864
};

int main()
{
  return 0;
}

omry@cyclop:~$ g++ 1.cpp

omry@cyclop:~$

Omry
I don't think this should compile, and, indeed, Comeau says: _error: a member of type "const float" cannot have an in-class initializer_. Seems like that's a GCC extension then.
sbi
g++ extension, use -ansi -pendantic and you'll get the error.
AProgrammer
+10  A: 

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;
Will
Some compilers will still expect you to have another line in the cpp file to define space for `LONG_CONST`: `const long MY_CONSTS::LONG_CONST;` Note that you don't repeat the initialization here.
Adrian McCarthy
@Adrian: the difference is that these compilers are broken.
sbi
+2  A: 

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...)

Adrien Plisson
+9  A: 

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.

JRL
That doesn't really answer the question. Why can a `long` be initialized and a `float` not? Presumable a `long` doesn't need 'to be stored in memory as [an object]' but a `float` does? How does that make sense?
KitsuneYMG
long constant can be made a part of the CPU instruction (at least on Intel CPUs). A float can't help being in memory. Using a slight abuse of notation, you can say that long's can be inlined, and floats cannot.
Seva Alekseyev
+16  A: 

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.

Tyler McHenry
Indeed. Good that C++0x will support in-class float initializers tho.
Johannes Schaub - litb
Another good way would be to make a function returning that value. The benefit is that the value is visible to the compiler (which can inline the function). An in-.cpp definition won't expose the value to other TUs.
Johannes Schaub - litb
There's another benefit to the function approach: you avoid the "static initialization order fiasco" that can happen if you try to initialize static values using other static values from other compilation units (although this will probably not happen with built-in types such as float).
Tyler McHenry
And in c++0x you can declare the return type as `constexpr float` [http://en.wikipedia.org/wiki/C%2B%2B0x#Generalized_constant_expressions]
KitsuneYMG
+1  A: 

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

Andreas Brinck
+2  A: 

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).

AProgrammer
A: 

Why are you using const with static here?

BreakHead
A: 

@Qutbuddin Kamaal

Why are you using const with static here?

I'm apparently not qualified to directly edit your question yet. Anyway, you use const to make sure the value doesn't change and static to allocate only one instance. Case in point, if it wasn't otherwise available, you could define the value of pi as a static const in a class so that it can't be changed and you don't have it allocated in each instance of the class in which it's defined. It's kind of like the C++ equivalent to a C #define without the pre-processing text substitution.

andand