Template static member initialization

A

ALiX

Hi!

My template class has a const static data member, which is used by the
constructor. It seems that the program does not produce the correct
result. Either it is the fault of the compiler (gcc 4.1) or I need to
learn more about initialization of static members... The code below
captures the problem and is as simple as I could make it:

#include <iostream>
#include <limits>

template<typename T> struct A {
const static unsigned NONE;
unsigned x;
A() { x = NONE; }
};

template<typename T> const unsigned A<T>::NONE =
numeric_limits<unsigned>::max();
A<double> a;

int main(void) {
cout << a.x << "\n";
cout << A<double>::NONE << "\n";
return 0;
}

The output of the program is:
0
4294967295

Mustn't NONE be initialized before a is constructed?

Cheers,
/ALiX
 
V

Victor Bazarov

ALiX said:
My template class has a const static data member, which is used by the
constructor. It seems that the program does not produce the correct
result. Either it is the fault of the compiler (gcc 4.1) or I need to
learn more about initialization of static members... The code below
captures the problem and is as simple as I could make it:

#include <iostream>
#include <limits>

template<typename T> struct A {
const static unsigned NONE;
unsigned x;
A() { x = NONE; }
};

template<typename T> const unsigned A<T>::NONE =
numeric_limits<unsigned>::max();

std::numeric_limits
A<double> a;

int main(void) {

Drop the 'void' -- bad style
cout << a.x << "\n";
cout << A<double>::NONE << "\n";
return 0;
}

The output of the program is:
0
4294967295

Mustn't NONE be initialized before a is constructed?

It's not *instantiated* unless it's "used". Perhaps referring to
it in the c-tor does not get it instantiated (and therefore does
not get it initialised), but I would consider it a compiler bug.

Try initialising it right in the class:

template<typename T> struct A {
static unsigned const NONE
= std::numeric_limits<unsigned>::max();
unsigned x;
A() : x(NONE) {}
};

V
 
A

ALiX

Try initialising it right in the class:

template<typename T> struct A {
static unsigned const NONE
= std::numeric_limits<unsigned>::max();
unsigned x;
A() : x(NONE) {}
};

Then I get an error:
error: 'std::numeric_limits<unsigned int>::max()' cannot appear in a
constant-expression
error: a function call cannot appear in a constant-expression

Making NONE non-const does not help either as non-const static members
cannot be initialized in the class declaration.

/ALiX
 
M

Michael DOUBEZ

ALiX a écrit :
Hi!

My template class has a const static data member, which is used by the
constructor. It seems that the program does not produce the correct
result. Either it is the fault of the compiler (gcc 4.1) or I need to
learn more about initialization of static members... The code below
captures the problem and is as simple as I could make it:

#include <iostream>
#include <limits>

template<typename T> struct A {
const static unsigned NONE;
unsigned x;
A() { x = NONE; }
};

template<typename T> const unsigned A<T>::NONE =
numeric_limits<unsigned>::max();
A<double> a;

int main(void) {
cout << a.x << "\n";
cout << A<double>::NONE << "\n";
return 0;
}

The output of the program is:
0
4294967295

Mustn't NONE be initialized before a is constructed?

My guess is that A<double>::NONE is not initialized at the creation of
a. It is the curse of static variable initialization order.

You can solve that by using a trait_style:

#include <iostream>
#include <limits>

template<typename T>
struct A_traits
{
static unsigned NONE(){return 0;};
};

template<>
struct A_traits<double>
{
static unsigned NONE(){return std::numeric_limits<unsigned>::max();}
};

template<typename T>
struct A
{
unsigned x;
A() { x = A_traits<T>::NONE(); }
};

A<double> a;

int main(void)
{
std::cout << a.x << "\n";
std::cout << A_traits<double>::NONE() << "\n";
return 0;
}
 
A

ALiX

This seems to work. However, it gives me another headache since in the
real application I'm writing, NONE is actually public (I should have
made it public in the example code... sorry!). I use it like
std::string::npos to return 'end-values'. I'd like a solution that
allows me to keep NONE as a public static member of the class A.

Cheers,
ALiX
 
V

Victor Bazarov

ALiX said:
This seems to work. However, it gives me another headache since in the
real application I'm writing, NONE is actually public (I should have
made it public in the example code... sorry!). I use it like
std::string::npos to return 'end-values'. I'd like a solution that
allows me to keep NONE as a public static member of the class A.

Don't initialise it with 'max()', initialise it with -1.

V
 
A

ALiX

Don't initialise it with 'max()', initialise it with -1.

Do I still need to define NONE outside the class declaration? Are we
always guaranteed that ((unsigned)-1) ==
numeric_limts<unsigned>::max()?

/ALiX
 
T

terminator

Hi!

My template class has a const static data member, which is used by the
constructor. It seems that the program does not produce the correct
result. Either it is the fault of the compiler (gcc 4.1) or I need to
learn more about initialization of static members... The code below
captures the problem and is as simple as I could make it:

#include <iostream>
#include <limits>

template<typename T> struct A {
const static unsigned NONE;
unsigned x;
A() { x = NONE; }

};

template<typename T> const unsigned A<T>::NONE =
numeric_limits<unsigned>::max();
A<double> a;

int main(void) {
cout << a.x << "\n";
cout << A<double>::NONE << "\n";
return 0;

}

The output of the program is:
0
4294967295

Mustn't NONE be initialized before a is constructed?

Cheers,
/ALiX

the order of initialization of static variables depends on link order
of modules and definition order of them in a module.
In general this is some nasty behavior that makes things complex.I am
not certain that this was the reason for your problem but I suggest to
enclose your static data in static functions:


template<typename T> struct A {
inline static const unsigned & NONE(void){//return by reference
static data=numeric_limits<unsigned>::max();
return data;
};
unsigned x;
A() { x = NONE(); }
};

int main(void) {
cout << a.x << "\n";
cout << A<double>::NONE()<< "\n";
return 0;
};

this way the compiler is forced to make sure that static data is
constructed before the first call to the enclosing static function.

regards,
FM.
 
A

ALiX

the order of initialization of static variables depends on link order
of modules and definition order of them in a module.
In general this is some nasty behavior that makes things complex.I am
not certain that this was the reason for your problem

I actually think that this specific case is a compiler issue. The
point is that the entire code is in a single translation unit, and so
the order of initialization is the order in which the variables are
defined. Therefore, NONE should be initialized before the global
variable a.

/ALiX
 
V

Victor Bazarov

ALiX said:
I actually think that this specific case is a compiler issue. The
point is that the entire code is in a single translation unit, and so
the order of initialization is the order in which the variables are
defined. Therefore, NONE should be initialized before the global
variable a.

The problem is that 'NONE' in your original code was a template (it was
a member of a template, and therefore a template itself). Those aren't
initialised were _defined_, they are initialised when first referenced
(IOW, when *instantiated*) [temp.inst]/1, /7. Since your use of 'NONE'
required (expected) it to be instantiated, you get into the chicked-and-
egg problem. To solve it, you could create a dummy object before the
declaration of the 'a' and initialise it with the NONE member:

unsigned const dummy = A<double>::NONE;

then if you happen to instantiate A<double> later, it will have access
to previously instantiated (and properly initialised) NONE member:

A<double> a;
...

V
 
V

Victor Bazarov

Victor said:
ALiX said:
I actually think that this specific case is a compiler issue. The
point is that the entire code is in a single translation unit, and so
the order of initialization is the order in which the variables are
defined. Therefore, NONE should be initialized before the global
variable a.

The problem is that 'NONE' in your original code was a template (it
was a member of a template, and therefore a template itself). Those
aren't initialised were _defined_, they are initialised when first
referenced (IOW, when *instantiated*) [temp.inst]/1, /7. Since your
use of 'NONE' required (expected) it to be instantiated, you get into
the chicked-and- egg problem. To solve it, you could create a dummy
object before the declaration of the 'a' and initialise it with the
NONE member:
unsigned const dummy = A<double>::NONE;

then if you happen to instantiate A<double> later, it will have access
to previously instantiated (and properly initialised) NONE member:

A<double> a;
...

Curiously enough, it might just be QoI issue. I built your code with
VC++ 2005 SP1. In Debug mode it gave me your output, and in Release
mode it gave the correct output... Go figure.

If I change the static to be initialised from a const expression (and
not a call to numeric_limits::max()), then it works OK in Debug as well
as in Release.

I've not tried other compilers.

V
 
J

James Kanze

It's not *instantiated* unless it's "used". Perhaps referring to
it in the c-tor does not get it instantiated (and therefore does
not get it initialised), but I would consider it a compiler bug.

The standard says rather clearly (in §3.6.2) that the order of
initialization of static variables in implicitly specialized
templates is unordered. In short, there is no guarantee that
the initialization will take place before the initialization of
a, in his code above.

In his code, the simplest solution would be to simply use static
initialization, i.e. initialize it with UINT_MAX. If the
initialization depended on the type, however, (e.g. was
std::numeric_limits<T>::max()), this is not an option. In such
cases, the best solution would probably be to use a static
member function.
Try initialising it right in the class:
template<typename T> struct A {
static unsigned const NONE
= std::numeric_limits<unsigned>::max();
unsigned x;
A() : x(NONE) {}
};

That's only legal if the initializer is a constant expression.
A constant expression cannot (at present, anyway) contain a
function call, even if the function is inline, and only returns
a constant value.
 
J

James Kanze

Do I still need to define NONE outside the class declaration? Are we
always guaranteed that ((unsigned)-1) ==
numeric_limts<unsigned>::max()?

Yes and yes. You can also use UINT_MAX, which is guaranteed to
be an integral constant expression.
 
J

James Kanze

I actually think that this specific case is a compiler issue. The
point is that the entire code is in a single translation unit, and so
the order of initialization is the order in which the variables are
defined.

And where is the variable A<double>::NONE defined? Nowhere in
your actual source code, obviously. All you've defined
concerning it are templates. Templates aren't classes,
functions or variables; they're just models to create such
things. At some point, the compiler will use the model to
generate the actual variable, but a priori, the compiler has no
way of knowing where that will be (or more concretely, whether
the instantiation finally used will be from this translation
unit, or from some other translation unit).

Because of this, the language specification specifically states
that implicit instantiations of static variables of a template
class are always unordered.
Therefore, NONE should be initialized before the global
variable a.

If you explicitly instantiate it, yes.
 
J

James Kanze

[...]
Curiously enough, it might just be QoI issue. I built your code with
VC++ 2005 SP1. In Debug mode it gave me your output, and in Release
mode it gave the correct output... Go figure.

What part of "undefined" don't you understand? The order of
initialization is undefined. I have no problem with the fact
that the code gives different results with different levels of
optimization.
If I change the static to be initialised from a const expression (and
not a call to numeric_limits::max()), then it works OK in Debug as well
as in Release.

All static initialization is always performed before any dynamic
initialization.
 
M

Michael DOUBEZ

ALiX a écrit :
Do I still need to define NONE outside the class declaration?

If it clarifies the purpose of A, you may alias it in your class:

template<typename T>
struct A
{
static const unsigned int NONE=A_traits<T>::NONE;
//...
};


Michael
 
P

Pete Becker

That's only legal if the initializer is a constant expression.
A constant expression cannot (at present, anyway) contain a
function call, even if the function is inline, and only returns
a constant value.

To elaborate a bit on the "at present, anyway": at last week's
standards meeting two proposals concerning constant expressions were
voted into the next standard. One permits calling appropriately
decorated functions as part of a constant expression, and the other
adds those decorations to various functions in the standard library,
including the members of the numeric_limits explicit instantiations. So
std::numeric_limits<unsigned>::max() will be acceptable as a static
initializer in C++0x.
 
T

terminator

Victor said:
ALiX wrote:
[...]

Curiously enough, it might just be QoI issue. I built your code with
VC++ 2005 SP1. In Debug mode it gave me your output, and in Release
mode it gave the correct output... Go figure.

What part of "undefined" don't you understand? The order of
initialization is undefined. I have no problem with the fact
that the code gives different results with different levels of
optimization.
If I change the static to be initialised from a const expression (and
not a call to numeric_limits::max()), then it works OK in Debug as well
as in Release.

All static initialization is always performed before any dynamic
initialization.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Why should the compiler allow a static variable be referenced before
inititialization? ( please do not speak about backward compatiblity)
IMHO the standard needs to change so that static initialization is
well-defined for all cases and conforms with OO.Every now and then I
am reading posts on similar problems.As long as C++ does not support
static ctors we need some semantics to initiate/finalize static stuff
in a clear order -IMHO conforming to the order by which super class
ctors in a herarchy queue are called.

regards,
FM.
 

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

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top