Static const integral data members can be initialized?

Discussion in 'C++' started by Immortal Nephi, Jul 22, 2010.

  1. 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;
    }
     
    Immortal Nephi, Jul 22, 2010
    #1
    1. Advertising

  2. Immortal Nephi

    Öö Tiib Guest

    On 23 juuli, 00:42, Immortal Nephi <> wrote:
    >         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;
    }
     
    Öö Tiib, Jul 22, 2010
    #2
    1. Advertising

  3. Immortal Nephi

    Ian Collins Guest

    On 07/23/10 09:42 AM, Immortal Nephi wrote:
    > 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.

    --
    Ian Collins
     
    Ian Collins, Jul 22, 2010
    #3
  4. Immortal Nephi

    TJorgenson Guest

    On Jul 22, 3:06 pm, Ian Collins <> wrote:
    > 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
     
    TJorgenson, Jul 22, 2010
    #4
  5. Immortal Nephi

    Ian Collins Guest

    On 07/23/10 10:17 AM, TJorgenson wrote:
    > On Jul 22, 3:06 pm, Ian Collins<> wrote:
    >> 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.


    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.

    --
    Ian Collins
     
    Ian Collins, Jul 23, 2010
    #5
  6. Immortal Nephi

    TJorgenson Guest

    On Jul 22, 4:03 pm, Ian Collins <> wrote:
    >
    > 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
     
    TJorgenson, Jul 23, 2010
    #6
  7. Immortal Nephi

    TJorgenson Guest

    On Jul 22, 4:49 pm, Ian Collins <> wrote:

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

    >
    > 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
     
    TJorgenson, Jul 23, 2010
    #7
  8. Immortal Nephi

    Ian Collins Guest

    On 07/23/10 12:21 PM, TJorgenson wrote:
    > On Jul 22, 4:49 pm, Ian Collins<> wrote:
    >
    >>>> 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.

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


    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!

    --
    Ian Collins
     
    Ian Collins, Jul 23, 2010
    #8
  9. Immortal Nephi

    TJorgenson Guest

    On Jul 22, 5:40 pm, Ian Collins <> wrote:
    >
    > 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).

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


    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
     
    TJorgenson, Jul 23, 2010
    #9
  10. * Ian Collins, on 23.07.2010 01:03:
    > On 07/23/10 10:17 AM, TJorgenson wrote:
    >> On Jul 22, 3:06 pm, Ian Collins<> wrote:
    >>> 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.

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

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Jul 23, 2010
    #10
  11. Immortal Nephi

    SG Guest

    On 22 Jul., 23:42, Immortal Nephi wrote:
    > 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
     
    SG, Jul 23, 2010
    #11
  12. Immortal Nephi

    James Kanze Guest

    On Jul 22, 11:06 pm, Ian Collins <> wrote:
    > On 07/23/10 09:42 AM, Immortal Nephi wrote:


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


    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.

    --
    James Kanze
     
    James Kanze, Jul 23, 2010
    #12
  13. Immortal Nephi

    James Kanze Guest

    On Jul 23, 1:40 am, Ian Collins <> wrote:
    > On 07/23/10 12:21 PM, TJorgenson wrote:
    > > On Jul 22, 4:49 pm, Ian Collins<> wrote:


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


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


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

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


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

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


    Or rather his colleagues are in the wrong job.

    --
    James Kanze
     
    James Kanze, Jul 23, 2010
    #13
  14. Immortal Nephi

    James Kanze Guest

    On Jul 23, 2:14 am, TJorgenson <> wrote:
    > On Jul 22, 5:40 pm, Ian Collins <> wrote:


    [...]
    > > "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?


    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.

    --
    James Kanze
     
    James Kanze, Jul 23, 2010
    #14
  15. Immortal Nephi

    James Kanze Guest

    On Jul 23, 1:30 pm, Pete Becker <> wrote:
    > On 2010-07-22 18:17:00 -0400, TJorgenson said:
    > > On Jul 22, 3:06 pm, Ian Collins <> wrote:
    > >> 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.


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

    --
    James Kanze
     
    James Kanze, Jul 23, 2010
    #15
  16. Immortal Nephi

    Bo Persson Guest

    TJorgenson wrote:
    > On Jul 22, 5:40 pm, Ian Collins <> wrote:
    >>
    >> 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).
    >


    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
     
    Bo Persson, Jul 23, 2010
    #16
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JFCM
    Replies:
    4
    Views:
    5,756
  2. Mike Hewson
    Replies:
    14
    Views:
    11,907
    Micah Cowan
    Jan 7, 2005
  3. Rakesh Sinha
    Replies:
    4
    Views:
    1,863
    Rakesh Sinha
    Jan 13, 2005
  4. Susan Baker
    Replies:
    2
    Views:
    524
    Alf P. Steinbach
    Jul 3, 2005
  5. , India
    Replies:
    5
    Views:
    1,792
    chisholm
    Feb 15, 2011
Loading...

Share This Page