Defining static members in C++
Asked Answered
M

6

32

I am trying to define a public static variable like this :

public :
         static int j=0;        //or any other value too

I am getting a compilation error on this very line : ISO C++ forbids in-class initialization of non-const static member `j'.

  1. Why is it not allowed in C++ ?

  2. Why are const members allowed to be initialized ?

  3. Does this mean static variables in C++ are not initialized with 0 as in C?

Thanks !

Magistrate answered 21/8, 2010 at 4:49 Comment(3)
Use static inlineRabbitry
Why?.. You have to know that C++ was created long ago when computer were 1000 time less powerful so it is sometime less perfect than newer language as many changes would break existing programs.Synchromesh
static constexpr is another alternative that could be used or enum for constant that are really constants...Synchromesh
S
22

(1.) Why is it not allowed in C++ ?

From Bjarne Stroustrup's C++ Style and Technique FAQ:

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

(2.) Why are const members allowed to be initialized ?

[dirkgently said it better]

(3.) Does this mean static variables in C++ are not initialized with 0 as in C?

As far as I know, as long as you declare the static member var in a .cpp it will be zero-initialized if you don't specify otherwise:

// in some .cpp
int Test::j; // j = int();
Safier answered 21/8, 2010 at 5:49 Comment(2)
Thanks for the great answer, you prevented me from pulling my hear out of my head! I've one question though, is there any possible way to supply a null pointer while defining reference members?Conclusion
@TimVisee Not sure if it's possible - at least in a portable, non UB way - but you'd end up with an invalid reference. If you want NULL semantics just use a pointer or maybe use something like Boost Optional.Safier
W
24

You will have to initialize the static variable in a .cpp file and not in the class declaration.

When you declare a static variable in the class, it can used without instantiating a class.

//Header file
class Test
{
  public:
    static int j;
};

//In cpp file

//Initialize static variables here.
int Test::j = 0;

//Constructor
Test::Test(void)
{
   //Class initialize code
}
Well answered 21/8, 2010 at 4:56 Comment(0)
S
22

(1.) Why is it not allowed in C++ ?

From Bjarne Stroustrup's C++ Style and Technique FAQ:

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

(2.) Why are const members allowed to be initialized ?

[dirkgently said it better]

(3.) Does this mean static variables in C++ are not initialized with 0 as in C?

As far as I know, as long as you declare the static member var in a .cpp it will be zero-initialized if you don't specify otherwise:

// in some .cpp
int Test::j; // j = int();
Safier answered 21/8, 2010 at 5:49 Comment(2)
Thanks for the great answer, you prevented me from pulling my hear out of my head! I've one question though, is there any possible way to supply a null pointer while defining reference members?Conclusion
@TimVisee Not sure if it's possible - at least in a portable, non UB way - but you'd end up with an invalid reference. If you want NULL semantics just use a pointer or maybe use something like Boost Optional.Safier
T
17

Why is it not allowed in C++ ?

Until and unless you define it, the variable doesn't become a l-value.

Why are const members allowed to be initialized ?

Even in this case, a definition is required if you are going to take the address of the variable.

9.4.2 Static data members

2 The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the member’s class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator. The initializer expression in the definition of a static data member is in the scope of its class

Also, this is primarily an usage artifact so that you can write:

class S {
      static const int size = 42;
      float array[ size ];
};

Does this mean static variables in C++ are not initialized with 0 as in C?

No they are:

3.6.2 Initialization of non-local variables

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zeroinitialized (8.5) before any other initialization takes place.

Though things get a bit more trickier in C++0x. All literal types can now be initialized (as opposed to only integral types in the current standard) which would mean that all scalar types (floats included) and some class types can now be initialized using an initializer in the declaration.

Tribe answered 21/8, 2010 at 5:41 Comment(1)
Why definition for static const integer is not required if the address is not taken ? It (static-const-int defined in .h) compiles,links successfully without definition. If so, since, when C++ allows this type of declaration without definition, since C++98 ? since C++03 ? Authentic sources please. C++ Standard (9.4.2) is not in sync with the compilers. It mentions the member shall still be defined if it is used in the program. So, I will tell you, I am not looking for quoting from the C++ Standard. What will be good is, GCC changelog/C++ Committee Notes.Inversely
V
9

The short answer:

It's equivalent to saying extern int Test_j = 0;.

If it did compile, what would happen? Every source file that includes your class's header file would define a symbol called Test::j initialized to 0. The linker tends not to like that.

Vidette answered 21/8, 2010 at 5:57 Comment(1)
this doesn't explain why you can define a static method in a class and then it be implicitly inline, but you you have to explicitly make a member inline or const. This implies that it's not about linker redefinition errorsRabbitry
S
2
class GetData        
{    
private:     
static int integer;   //Static variable must be defined with the extension of keyword static;      
public:      
static void enter(int x)      
{       
integer = x;  //static variable passed through the static function    
}
static int  show()   //declared and defined
{
    return integer;   //will return the integer's value
}        
};        
int GetData::integer = 0;    //Definition of the static variable       
int main()      
{      
   GetData::enter(234);    //value has been passed through the static function enter. Note that class containing static variables may not have the object in main. They can be called by scope resolution operator in main.
   cout<<GetData::show();      
}     
Syringe answered 17/5, 2016 at 14:28 Comment(0)
R
0

The question is more why do you have to explicitly make a static member defined within the class const or inline, when you don't have to do that for a static method, which is automatically made inline when defined within the class. More broadly, the question is why does defining a method in a class make it inline and why can't defining a static member in a class be treated as either inline or like a file scope class scope global symbol like an out-of-line definition, instead of making it a compiler error.

When you define it static const, it will optimise out the load and if you force it to load the value by making it volatile const or taking the address of it, you will get a linker error because it never emits the symbol (clang will force you to define a static volatile const out of line, but gcc won't). This is much unlike a const at file scope.

What this says to me is that the default for a static member is to not emit a symbol at all, rather than be inline. const allows the value to be optimised into the code as an immediate (which is why it actually lets you define it as const, because if you didn't then you'd be left with no symbol and then references to that missing symbol) but still the compiler does not emit a symbol. But why is this? Why not just make it inline by default, like a method, instead of emitting no symbol at all?

The only difference it has to a method is that the same definition can have multiple different values -- if you defined the class type in an anonymous namespace it would have static (local) linkage and then you could avoid the redefinition errors. Now, it would have multiple different states in the different translation units if it were included into the translation units but a method would not (it would have multiple definitions but those definitions would be identical). The same is true with non static linkage, where one of the definitions will be arbitrarily selected if the method is inline, otherwise there will be a multiple definition error. With static linkage, it also still prevents you from defining a static member in the class without inline, because it still would not emit a symbol.

Because it prevents you from defining a member (i.e. does not emit a symbol) when it would (static linkage) and wouldn't (extern linkage therefore linker error) have multiple value states, and when there would be a linker error (extern linkage) and when there wouldn't be a linker error (static linkage), it can't be to do with these properties.

So you have to ask why the intention of a member defined in a class would be out-of-line (i.e. like a regular file scope symbol), and the intention of a method defined in a class be inline (i.e. emit symbol only if it is used in the translation unit, and select only one symbol in the comdat group to use). I mean, when a programmer defines a static method in a class, it's assumed to be the same method, but when they define a member, it could have many different values, which is an issue for comdat linkage, because you don't know which symbol it is going to select, and will be a silent logic error:

// file.cpp
#include <iostream>

struct c {
    static inline int k = 3;
};

int func(){
    std::cout << c::k;
    return 0;
}
// main.cpp
#include <iostream>

struct c {
    static inline int k = 4;
};

int main() {
    std::cout << c::k; // prints 3
}

If it forces you to define it out of line, you will instead get a linker redefinition error, making it easier to diagnose. Admittedly, the same issue would arise if the method definitions were different, and the whole assumption here is that the class definition is supposed to be in a header file -- so why would the definition ever be different.

Making the static member definition in the class behave like an out-of-line definition would cause linker errors if you include it in multiple files, so it is not interpreted to be out-of-line by default, due to the annoyance. I do not however see any reason why it isn't made implicitly inline like a static method defined in the class is though.

I do not think there is an answer other than 'that's how it is'. It's a bit like why you can't use static to give a method static linkage (and it's an error, or the static is ignored completely on -fpermissive) but you can enclose it in an anonymous namespace to achieve that (and the anonymous namespace has to surround the definition and the declaration (so the class), not just the definition (or not just the declaration -- but that's impossible anyways because if the declaration is in an anonymous namespace then the definition has to be or it won't compile)). I haven't seen any such answer on SO as of yet.

Rabbitry answered 2/4, 2021 at 4:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.