29

In the class:

class foo
{
public:
    static int bar; //declaration of static data member
};

int foo::bar = 0; //definition of data member

We have to explicitly define the static variable, otherwise it will result in a

undefined reference to 'foo::bar'

My question is:

Why do we have to give an explicit definition of a static variable?


Please note that this is NOT a duplicate of previously asked undefined reference to static variable questions. This question intends to ask the reason behind explicit definition of a static variable.

13
  • 6
    If this class is in a header that is compiled with three separate translation units, how do you ensure that the ODR isn't violated unless only one translation unit has the definition, outside of the header? Commented Mar 24, 2015 at 2:28
  • 1
    Some (read: exactly one) compiling unit needs to contain the reference to foo::bar. Commented Mar 24, 2015 at 2:28
  • 2
    @volerag, It's pretty much just based on the One Definition Rule. You can think of a translation unit as one .cpp file that you compile along with every recursively included header file "pasted" into the top of that .cpp file. Things defined in headers are bound to be defined more than once when you start combining the translation units unless they are allowed to be (like class definitions) or special care is taken (like the variable in question here). Commented Mar 24, 2015 at 2:34
  • 3
    This gets more interesting with C++11 which allows in class initialization and so in many cases you don't need an external definition unless it is odr used but figuring out when something is odr-used can be very tricky in many cases. Commented Mar 24, 2015 at 2:40
  • 1
    con't and then you see some really wacky cases like this one from the draft C++14 standard. Commented Mar 24, 2015 at 2:55

5 Answers 5

30
+50

From the beginning of time C++ language, just like C, was built on the principle of independent translation. Each translation unit is compiled by the compiler proper independently, without any knowledge of other translation units. The whole program only comes together later, at linking stage. Linking stage is the earliest stage at which the entire program is seen by linker (it is seen as collection of object files prepared by the compiler proper).

In order to support this principle of independent translation, each entity with external linkage has to be defined in one translation unit, and in only one translation unit. The user is responsible for distributing such entities between different translation units. It is considered a part of user intent, i.e. the user is supposed to decide which translation unit (and object file) will contain each definition.

The same applies to static members of the class. Static members of the class are entities with external linkage. The compiler expects you to define that entity in some translation unit. The whole purpose of this feature is to give you the opportunity to choose that translation unit. The compiler cannot choose it for you. It is, again, a part of your intent, something you have to tell the compiler.

This is no longer as critical as it used to be a while ago, since the language is now designed to deal with (and eliminate) large amount of identical definitions (templates, inline functions, etc.), but the One Definition Rule is still rooted in the principle of independent translation.

In addition to the above, in C++ language the point at which you define your variable will determine the order of its initialization with regard to other variables defined in the same translation unit. This is also a part of user intent, i.e. something the compiler cannot decide without your help.


Starting from C++17 you can declare your static members as inline. This eliminates the need for a separate definition. By declaring them in that fashion you effectively tell compiler that you don't care where this member is physically defined and, consequently, don't care about its initialization order.

Sign up to request clarification or add additional context in comments.

11 Comments

I just have a basic question @AnT , why should or should not static variables be defined in the header containing the class definition containing the static variable?
@volerag: No, absolutely not. Static members have to be defined in implementation files (each member - in one and only one implementation file of your choice), not in header files.
@volerag: Defining it in the header file will produce multiple definitions if the header file gets included into several implementation files (which is the purpose of header files). This is an ODR violation, an error. In other words, it is the same as defining an ordinary variable in a header file - it will result in an ODR error.
@volerag: Guard block does not protect you from ODR violations. Guard block has nothing to do with it at all. Guard block prevents you from including the header into the same translation unit more than once. It does not change anything with regard to including the header into different translation units. And ODR violations come specifically from inclusion into different translation units. The rule is: don't define external variables or external non-inline functions in header files. There's no way around this rule.
@volerag note the part of the answer that states this is no longer a critical reason. Local static variables are allowed inside inline functions, and those variables will all refer to the same object no matter how many places they are called from, even from different source files. If the C++ committee decided to drop this requirement they could at any time.
|
4

In early C++ it was allowed to define the static data members inside the class which certainly violate the idea that class is only a blueprint and does not set memory aside. This has been dropped now.

Putting the definition of static member outside the class emphasize that memory is allocated only once for static data member (at compile time). Each object of that class doesn't have it own copy.

Comments

1

static is a storage type, when you declare the variable you are telling the compiler "this week be in the data section somewhere" and when you subsequently use it, the compiler emits code that loads a value from a TBD address.

In some contexts, the compiler can drive that a static is really a compile time constant and replace it with such, for example

static const int meaning = 42;

Inside a function that never takes the address of the value.

When dealing with class members, however, the compiler can't guess where this value should be created. It might be in a library you will link against, or a dll, or you might be providing a library where the value must be provided by the library consumer.

Usually, when someone asks this, though, it is because they are misusing static members.

If all you want us a constant value, e.g

static int MaxEntries;
...
int Foo::MaxEntries = 10;

You would be better off with one or other of the following

static const int MaxEntries = 10;
 // or
enum { MaxEntries = 10 };

The static requires no separate definition until something tries to take the address of or form a reference to the variable, the enum version never does.

Comments

0

Inside the class you are only declaring the variable, ie: you tell the compiler that there is something with this name. However, a static variable must get some memory space to live in, and this must be inside one translation unit. The compiler reserves this space only when you DEFINE the variable.

Comments

0

Structure is not variable, but its instance is. Hence we can include same structure declaration in multiple modules but we cannot have same instance name defined globally in multiple modules.

Static variable of structure is essentially a global variable. If we define it in structure declaration itself, we won't be able to use the structure declaration in multiple modules. Because that would result in having same global instance name (of static variable) defined in multiple modules causing linker error "Multiple definitions of same symbol"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.