Static const integral data members can be initialized?

I

Immortal Nephi

Why do Microsoft C++ Compiler reports:

error C2864: 'obj::y' : only static const integral data members can be
initialized within a class

float and double keywords are not allowed to be in static const
inside class.

struct obj {
static const int x = 5; // OK
static const double y = 15.0; // c2864
};

int main() {
obj o;

return 0;
}
 
Ö

Öö Tiib

        Why do Microsoft C++ Compiler reports:

error C2864: 'obj::y' : only static const integral data members can be
initialized within a class

        float and double keywords are not allowed to be in static const inside class.

Not quite true. They can be static const inside a class but they can
not be initialized within class definition.

struct Obj
{
static double const y_;
};

double const Obj::y_ = 15.0;

int main()
{
Obj o;
return 0;
}
 
I

Ian Collins

Why do Microsoft C++ Compiler reports:

error C2864: 'obj::y' : only static const integral data members can be
initialized within a class

float and double keywords are not allowed to be in static const
inside class.

float and double are not integral types.

Their representation my differ between the build host and the deployment
environment.
 
T

TJorgenson

On 07/23/10 09:42 AM, Immortal Nephi wrote:

float and double are not integral types.

Their representation my differ between the build host and the deployment
environment.

And how exactly does initialization outside the class definition help
in this regard?

It seems to me that allowing initialization of static const float and
double within a class would not break anything and would be a useful
construct. I believe the problem you mention would be the same either
way.

Tony
 
I

Ian Collins

And how exactly does initialization outside the class definition help
in this regard?

It seems to me that allowing initialization of static const float and
double within a class would not break anything and would be a useful
construct. I believe the problem you mention would be the same either
way.

I'm sure a resident language lawyer can quote the chapter and verse, but
from a practical point of view, an initialisation within a class is a
compile time constant. The compiler can substitute the literal value as
an optimisation. Initialisations outside of the class are performed at
run time before main is called.
 
T

TJorgenson

I'm sure a resident language lawyer can quote the chapter and verse, but
from a practical point of view, an initialisation within a class is a
compile time constant.  The compiler can substitute the literal value as
an optimisation.  Initialisations outside of the class are performed at
run time before main is called.

So you are saying that the following class definition:

class D
{
static const double d;
};

with the following definition in a cpp file:

const double D::d = 0.1;

could cause the compiler to generate initialization code for this
constant that runs before main is called to generate the constant in
the format for the target processor that may be different from the
host that the compiler runs on. Agreed.

But if the compiler can generate this initialization code, I contend
that it could also generate the constant in the correct target format
as well. After all, the compiler needs to know the target processor
instruction set to generate the code. I still don't think this
justifies why floats and doubles can't be initialized within a class
definition.

Tony
 
T

TJorgenson

But where do you stop?  If floating point values can be compile time
constants, can they be used in switch cases?  As template parameters?

I get your point, but I'm not arguing for that. Switch cases with
floats would essentially require automatic generation of direct
equality comparisons, which could be problematic. I also understand
that allowing template parameters to be floats and doubles could cause
the generated code to have behavior that is processor/compiler/
compiler-option dependent. This would be bad IMO.

I just think initialization of all static constants could be allowed
within a class. I also think that the standard should not require that
constants that are initialized within a class also be defined outside
the class, unless the code explicitly takes the address of the
constant somewhere. I would expect a link error for that.

We have been getting away with not defining static const integers
outside the class in most cases even though it is strictly illegal,
but some constructs seem to generate code that requires the out-of-
class definition. For example, with our latest compiler using static
const integers in a conditional operator (?:) seems to require the out-
of-class definition if the constants are any integer types other that
int and unsigned int. This is a PITA IMO and it makes it hard for me
to sell my colleges on the idea that it is better to use static const
integers rather than #define. While it is true that this is
essentially just syntactic sugar after all, in this case it would make
it easier to sell a more C++ idiomatic solution for constants to those
looking to save keystrokes.

Tony
 
I

Ian Collins

I get your point, but I'm not arguing for that. Switch cases with
floats would essentially require automatic generation of direct
equality comparisons, which could be problematic. I also understand
that allowing template parameters to be floats and doubles could cause
the generated code to have behavior that is processor/compiler/
compiler-option dependent. This would be bad IMO.

I just think initialization of all static constants could be allowed
within a class.

OK, consider how a compiler should evaluate

static const double d = 42.42*3.5/6;
I also think that the standard should not require that
constants that are initialized within a class also be defined outside
the class, unless the code explicitly takes the address of the
constant somewhere. I would expect a link error for that.

The wording of 9.4.2/4 is:

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

"used in the program" is is rather woolly.
We have been getting away with not defining static const integers
outside the class in most cases even though it is strictly illegal,
but some constructs seem to generate code that requires the out-of-
class definition. For example, with our latest compiler using static
const integers in a conditional operator (?:) seems to require the out-
of-class definition if the constants are any integer types other that
int and unsigned int. This is a PITA IMO and it makes it hard for me
to sell my colleges on the idea that it is better to use static const
integers rather than #define.

If your colleagues (I assume you made a typo!) want to use #define, you
are in the wrong job!
 
T

TJorgenson

OK, consider how a compiler should evaluate

   static const double d = 42.42*3.5/6;
Ideally, the compiler should evaluate double constants the same way
the target environment will evaluate doubles, but this is still not
relevant to _where_ this constant expression lives (i.e. in the class
or outside).
The wording of 9.4.2/4 is:

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

Thanks for the reference.
"used in the program" is is rather woolly.

I still think the standard could restrict "if it is used in the
program" to be "if its address is taken in the program". What other
"use in the program" (sic) should require a definition at namespace
scope?

I think it is rare to take the address of a static constant integer
and if you do so without providing the out-of-class definition you
will get a linker error that will be immediately fixed. The standard
language makes it the compiler writer’s job to decide when the out-of-
class definition is required, meaning that code may work now, but some
day somebody adds code that requires that definition and then we get
the linker error and have to explain why the definition wasn't
necessary before (even though it technically was).

Tony
 
A

Alf P. Steinbach /Usenet

* Ian Collins, on 23.07.2010 01:03:
I'm sure a resident language lawyer can quote the chapter and verse, but
from a practical point of view, an initialisation within a class is a
compile time constant. The compiler can substitute the literal value as
an optimisation. Initialisations outside of the class are performed at
run time before main is called.

Well, I'm not a language lawyer, but I'm good at common sense.

One can easily obtain the same effect as initialization within the class
definition, e.g., for the OP's code, off the cuff,

template< class Dummy >
struct obj_constants
{
static int const x;
static double const y;
};

template< class Dummy >
int const obj_constants< Dummy >::x = 5;

template< class Dummy >
int const obj_constants< Dummy >::y = 15.0;

struct obj
public obj_constants< void >
{
// Whatever more.
};

Hence any argument based on this generating wrong code is invalid.

As I recall the problem with initializations-in-class was just the vicarious can
of worms argument that you mention later down-thread,

"But where do you stop? If floating point values can be compile time
constants, can they be used in switch cases? As template parameters?"

i.e. political.

No, you're not allowed to build an annex, because where would it stop? Next we'd
have to allow you to build a busy airport, to great annoyance to your
neighbours. Also, you can't take up painting as a profession, because if we
allowed that and everybody chose to be painters, then nobody would produce food
for the world and we'd all starve to death. And so on. Where would it stop?


Cheers & hth.,

- Alf
 
S

SG

Why do Microsoft C++ Compiler reports:

error C2864: 'obj::y' : only static const integral data members
can be initialized within a class

Because the ISO C++ standard says so. But if you're looking for a
reason why it is the way it is, I think there is no good answer.

But you will be pleased to know that C++0x will extend this rule to
all "literal types" including floats and even user-defined literal
types (such as std::complex<double> for example).

Keep in mind that an in-class initializer for static const members is
not a substitute for a definition. So, if you try to take the address
of such an object or bind it to a reference you have to provide a
definition as well.

Cheers!
SG
 
J

James Kanze

On 07/23/10 09:42 AM, Immortal Nephi wrote:
float and double are not integral types.

But they can still be declared as static const inside a class.
Their representation my differ between the build host and the
deployment environment.

That's true for integral types as well. The official
justification for not allowing them here is that an in range
floating point literal might represent a different value in the
two environments (which isn't true of integral types, which are
always exact); i.e. the exact value of something like:
double const pi = 3.14159;
will be different on a PC than on an IBM mainframe.
 
J

James Kanze

OK, consider how a compiler should evaluate
static const double d = 42.42*3.5/6;

The same way it would if this were a variable at namespace
scope.
The wording of 9.4.2/4 is:

The wording in the draft of C++0x has changed considerably. It
allows the initializer for all "literal types". All scalar
types are literal types, as are some class types, so you can
write things like:

class POD { int a; int b; };

class Toto
{
static double const d = 3.14159;
static POD const c = { 42, -1 };
// ...
};

(if I've understood correctly), and an expression like Toto::c.a
would be an integral constant expression.
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 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.
"used in the program" is is rather woolly.

§3.2/2:
An expression is potentially evaluated unless is the
operand of the sizeof operator (5.3.3), or is the
operand of the typeid operator and does not designate
an lvalue of polymorphic class type (5.2.8). An object
or non-overloaded function is used if its name appears
in a potentially-evaluated expression. [...]

The last "used" is in italics, so this is a definition of the
term. (The text goes on to define what used means for other
things, like virtual functions, etc.)
If your colleagues (I assume you made a typo!) want to use
#define, you are in the wrong job!

Or rather his colleagues are in the wrong job.
 
J

James Kanze

On Jul 22, 5:40 pm, Ian Collins <[email protected]> wrote:

[...]
I still think the standard could restrict "if it is used in
the program" to be "if its address is taken in the program".
What other "use in the program" (sic) should require
a definition at namespace scope?

Creating a reference to it?

Formally, a variable (const or not) is an lvalue. Anytime the
lvalue is not immediately converted to an rvalue, you would need
the definition. In theory, at least.

Note that if you have something like:

struct Toto { static int const a = 42; };

and somewhere write:

std::vector<int> v;
v.push_back(Toto::a);

you need the definition, since vector::push_back uses pass by
reference. (In practice, vector::push_back is probably inlined,
and if the compiler actually inlines it, then it might not
really need the reference.)
I think it is rare to take the address of a static constant integer

Perhaps not that rare, if you remember that pass by reference
counts as "taking the address", and that a lot of templated
functions regularly use pass by reference, even when the
template is instantiated on an int.
and if you do so without providing the out-of-class definition
you will get a linker error that will be immediately fixed.

Typically, for something like v.push_back, you'll only get the
linker error if the compiler doesn't inline the code for some
reason.
The standard language makes it the compiler writer’s job to
decide when the out-of-class definition is required,

No. The standard language says that if the out-of-class
definition is absent, it is undefined behavior. Which means
that it's up to the user to get it right; the compiler writer
doesn't have to do anything.
meaning that code may work now, but some day somebody adds
code that requires that definition and then we get the linker
error and have to explain why the definition wasn't necessary
before (even though it technically was).

Technically, the definition was necessary; the compiler just
didn't detect the error. What you're proposing has the opposite
effect: the definition might not be necessary now, but some
change in the use of the class would make it necessary.

The simplest rule is to always provide the definition,
regardless of what the standard or the compiler require.
 
J

James Kanze

The difference is that floating-point values are not
constant-expressions (in the terminology of the language definition).

You mean integral constant expressions, I think.
A floating-point literal is a constant expression (at least for
the purpose of non-local static object initialization).
That means that they can't be used in places where a
constant-expression is required, such as the size of an array:
const int size = 1;
int arr[size]; // OK
const double dsize = 1.0;
int darr[(int)dsize]; // error: dsize not a constant-expression

Interestingly enough,
int darr[(int)3.14159];
is legal.
Initialization within a class definition was originally
designed to support constant-expressions, not as a general
initialization technique.

Yes. Originally, IIRC, allowing the initialization expression
in the class definition was considered a hack, necessary to
support things like:
struct Toto
{
static int const size = 10;
char array[size];
};
, but not something one would really want to use if one could
avoid it.
 
B

Bo Persson

TJorgenson said:
Ideally, the compiler should evaluate double constants the same way
the target environment will evaluate doubles, but this is still not
relevant to _where_ this constant expression lives (i.e. in the
class or outside).

Constants yes, but not expressions. If you require multiplication and
division, why not trigonometric "constants"?

If your d is a constant, so is std::sin(d). A cross compiler is
allowed to pass on that, and not produce the exact bit pattern of the
target's 128-bit long double. It can postpone that until run-time (on
the proper harware).

So, it is a constant, but not a compile time constant.



Bo Persson
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top