Why Aren't Static Const Floats Allowed

Why aren't static const floats allowed?

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.

How is in-class static const initialization of float different from int in C++?

It's just a limitation of the language, and one that has been addressed by the introduction of generalized constant expressions.

Since the original C++, only static class member constants of integral type can be initialized inline; this is the is same type restriction as for non-type template parameters. So you can combine the two like this:

struct MyTrait { static const int value = 10; };

template <int N> struct Foo;

Foo<MyTrait::value> foo;

In this usage, the static constant is not odr-used and no definition is required. I'm speculating, but I can imagine that this kind of use was the primary intention of allowing inline initialization. For all other types, you would presumably want to have a definition anyway, so you might as well put the initializer in the definition.

This isn't an excuse, of course, and I suppose the introduction of constexpr seeks to rectify this original narrow-mindedness.

Why can't I have a non-integral static const member in a class?

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

Why are inlined static consts not allowed, except ints?

The int and the short are legal, and if your compiler doesn't allow them then your compiler is bust:

9.4.2/4: ... If the 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.

I believe that the reason that floats and doubles aren't treated specially as constants in the C++ standard, in the way that integral types are, is that the C++ standard is wary that the arithmetic operations on float and double could be subtly different on the compiling machine, than they are on the machine that executes the code. For the compiler to evaluate a constant expression like (a + b), it needs to get the same answer that the runtime would get.

This isn't so much of an issue with ints - you can emulate integer arithmetic relatively cheaply if it differs. But for the compiler to emulate floating-point hardware on the target device might be very difficult. It might even be impossible, if there are different versions of the chip and the compiler doesn't know which the code will run on. And that's even before you start messing with the IEEE rounding mode. So the standard avoided requiring it, so that it didn't have to define when and how compile-time evaluation can differ from runtime evaluation.

As Brian mentions, C++0x is going to address this with constexpr. If I'm right about the original motivation, then presumably 10 years has been long enough to work through the difficulties in specifying this stuff.

why initialization of static member variable is not allowed inside class but initialization of const static members is allowed?

One needs a bit of space in memory. Const do not - they can be hard coded.

Is there reason we are able to define [static const int] in a class definition, but not other static const types?

Static constants of integral types can be initialized inside a class definition. That doesn't mean that the object actually exists, since you haven't provided a definition yet, but because the compiler knows the value of the object, you can sometimes get away with it.

That is, if you're not attempting to take the address of the variable or pass it by reference, but only use its value, then you don't need to provide a defintion at all, and the compiler simply substitutes the value wherever you use the variable.

C++11 introduces the constexpr keyword which allows you to do the same for a much wider variety of types.

Undefined reference to static const double when used with complex.h

...how to make it work...?

#include <iostream>
#include <complex>

using namespace std;

class Test {
static const double dt;
public:
void func();

};

//move initialization outside of class
const double Test::dt = 0.1;

void Test::func() {
cout << dt << endl; // OK!
cout << dt*complex<double>(1.0, 1.0) << endl; // Undefined reference

}

int main() {
Test a;
a.func();
}


OR (see this question for explanations)

class Test {
static const double dt = 0.1;
public:
void func();

};
const double Test::dt;


OR (same trick as the one above has, but with c++11's constexpr)

class Test { 
static constexpr double dt = 0.1;
public:
void func();

};
constexpr double Test::dt;


Any ideas why this error happens...?

From here:

If a static data member of integral or enumeration type is declared
const (and not volatile), it can be initialized with a initializer in
which every expression is a constant expression, right inside the
class definition...

So static data member could be initialized inside of class definition if it's of the type int or enum and declared const, which isn't your case. ( see this answer for more info )

Why it's seems to be working for first line? Well, compiling with clang I got:

warning: in-class initializer for static data member of type 'const
double' is a GNU extension

So this float type initialization is extension of gcc compiler, and this extension is probably won't work with function's that expecting reference type argument (just a guess for now).

Also note, that this applies to c++98 only (c++11 has constexpr keyword that is addressing this issue)



Related Topics



Leave a reply



Submit