3
$\begingroup$

I am new to C++ and just learned that "The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope."

I am curious about why this is. Are there particular design advantages to making the rules of the language in this way? I am not asking: "tell me what Stroustrup was thinking when he made C++" -- which I know is an unfair question on these forums. I am asking: if someone sat down to make a computer language, why might they require that static data members are defined outside of the class. What are the advantages of this? What are the disadvantages? What kinds of costs would this incur? Why would someone put this quirk in their language? (Also I'm curious, but I know it's not totally appropriate to ask here: how did this quirk end up in C++).

$\endgroup$
  • $\begingroup$ Note that Java and Ruby do include static members in the class definition, and Scala uses a whole different concept. So the decision it not at all clear-cut. $\endgroup$ – Raphael Sep 16 '13 at 7:49
3
$\begingroup$

Such language design solution makes sense, because class body is basically a declaration, which normally has to be in the header file. All definitions has to appear in cpp files, where class instances are actually used. If you allow static members to be defined inside class body, then you mix declarations with definitions. So I would require this to make separation between declarations and definitions clear.

In terms of this separation it is fine to initialise static const class members inside class body. Basically, you are defining some constant and it could be equivalently done with some #define directive.

It doesn't seem that this restriction is due to the limits of the language or a compiler. Java doesn't have this restriction and allows definitions inside the body of a class for any static data member (not const only).

Update: C++11 relaxes the restriction and supports non-static member in-class initialisation. Static members still have to be defined outside.

$\endgroup$
  • 1
    $\begingroup$ You answer the question using another idiosyncracy of C(++), so this does not the general answer the OP is looking for. $\endgroup$ – Raphael Sep 16 '13 at 7:53
3
$\begingroup$

In C++ it is generally the case that you can access the variables that are declared inside whatever level of curly braces you are currently in. Curly braces and scope at least roughly correspond.

But a data definition includes an initializer, which is code. The code that initializes the static data member is not allowed to access any of the non-static data members of the class. (Because those are members of objects, and there is a one-to-many relationship between the static member and the objects and in any case the static member is initialized before any of the objects of the class are created.)

So example:

static int a=0;

class foo {
  int b;
  static int c = b+1;  // what does this mean????
};

int foo::c = a+2;  // This makes sense.  We are initializing using only other statics.

Let's look at another similar situation (from C) where the language designers made a different decision that has historically caused a lot of trouble. In C static usually means "has the lifetime from the time the compilation unit is loaded until the compilation unit is unloaded." Unfortunately there is one case where it means something different:

static int y = 0;
int foo(int x) {
  static int bar = x+y; // bar gets initialized when???  And what value does it get?
  bar += x;
  return bar;
}

When does bar get initialized? At the time of the first call to foo! So its initial value depends on x (which kind of makes sense) and whatever the value of y is at the moment (which is confusing at best.) This also has performance and correctness implications because of multithreading: foo's initializer needs to be protected with a guard that guarantees the initializer gets executed only once, and the guard needs to be atomic with respect to multithreading! So the above code is equivalent to the following:

static int y = 0;
static int foo_initializer_has_been_called=false;
int foo(int x) {
  static int bar;
  if (foo_initializer_has_been_called) {     // actually this needs to be an atomic
    bar = x+y;                               // test-and-test-and-set to handle
    foo_initializer_has_been_called = true;  // multithreading
  }
  bar += x;
  return bar;
}

Arguably the designers of C had no choice because there was no other place to write the initializer for a variable with static lifetime but local scope. The designers of C++ couldn't fix the mistake that the C designers made, but they could avoid making it a second time, because they have the class_name::member_name syntax to use outside the class declaration. So a static declared inside a class has exactly the same lifetime as a static declared outside the class, and the initializer is written at the global scope to make it clear that the lifetime and scope of accessible variables during initialization is the same as it would be if the static was declared outside the class.

$\endgroup$
  • $\begingroup$ That does not explain why you want to define stuff outside of the class definition. It rather seems to be evidence that the compiler is weak (or the designer assumes the programmer is dumb). $\endgroup$ – Raphael Sep 16 '13 at 7:51
  • $\begingroup$ @Raphael: you've got it backwards. The class foo { ... } is a declaration, not a definition. As a convenience Some definitions are permitted inside the class declaration when it is unambiguous what they mean. The argument is not that the programmer is dumb, but rather that the language shouldn't encourage one programmer to write code that will mislead a different programmer. $\endgroup$ – Wandering Logic Sep 16 '13 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.