From: Greg Chicares (chicares_at_[hidden])
Date: 2001-07-18 21:32:14


Michael Kenniston wrote:
>
> template< typename Rep, typename Tag >
> struct constant
> {
> constant() {}
> operator Rep() const; // fully specialized for each Rep/Tag pair
> };

> - there are no "static-initialization-order" issues, because the static
> variables contain no data to initialize

Does this solve the initialization order problem? Consider

> namespace double_constants
> {
> constant< double, pi_tag > const pi;
> constant< double, e_tag > const e;
> }

The 'constant' struct has a nontrivial constructor, so the lifetime of
'pi' begins begins when its constructor finishes. If we add at global
scope in a different source file

  double global_pi_squared = std::pow(double_constants::pi, 2);

and the compiler chooses to initialize that before 'pi', then 'pi' is
converted to an rvalue, but isn't that undefined behavior?

Can that be fixed by removing the user-defined constructor from
'constant' and the const-qualifier from 'pi'? We don't care whether
'pi' is constant as long as its operator Rep() is.

> Here's the general idea (tested under MSVC6 and g++2.95.3-4):

If you write 'template<>' before the four lines like this one:

inline constant< float, pi_tag >::operator float() const { return 3.14F;}

then it compiles OK with
  Comeau C/C++ 4.2.45.2
  gcc-3.0x (2001-06-26 CVS)
  gcc-2.95.2-1
  borland C++ 5.5.1

Also suggest removing unused argument 'dummy' from
  void show_all( const T & dummy, const char * label )

> Well, ok, if you /really/ want defaults, I guess you could replace
> the master declaration of the conversion operator with something like:
>
> operator Rep() const { return Rep( constant< long double, Tag >() );
> }

Specifying the value once with maximal precision and relying on standard
floating-point conversions is an attractive idea. But the value you get
can depend on the hardware direction, at least on an 80x87, so it can
change during program execution.