How to Initialize Static Members in the Header

How to initialize static members in the header

You can't define a static member variable more than once. If you put variable definitions into a header, it is going to be defined in each translation unit where the header is included. Since the include guards are only affecting the compilation of one translation unit, they won't help, either.

However, you can define static member functions! Now, at first sight that may not look as if it could help except, of course, that function can have local static variable and returning a reference to one of these behaves nearly like a static member variable:

static std::string& bstring() { static std::string rc{"."}; return rc; }

The local static variable will be initialized the first time this function is called. That is, the construction is delayed until the function is accessed the first time. Of course, if you use this function to initialize other global objects it may also make sure that the object is constructed in time. If you use multiple threads this may look like a potential data race but it isn't (unless you use C++03): the initialization of the function local static variable is thread-safe.

How to initialize private static members in C++?

The class declaration should be in the header file (Or in the source file if not shared).

File: foo.h

class foo
{
private:
static int i;
};

But the initialization should be in source file.

File: foo.cpp

int foo::i = 0;

If the initialization is in the header file then each file that includes the header file will have a definition of the static member. Thus during the link phase you will get linker errors as the code to initialize the variable will be defined in multiple source files.
The initialisation of the static int i must be done outside of any function.

Note: Matt Curtis: points out that C++ allows the simplification of the above if the static member variable is of const integer type (bool, char, char8_t [since C++20], char16_t, char32_t, wchar_t, short, int, long, long long, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants.). You can then declare and initialize the member variable directly inside the class declaration in the header file:

class foo
{
private:
static int const i = 42;
};

Why can't I initialize a static class member in a common header file?

You have chosen the "non-inline" mode of initializing your static member. That is perfectly legitimate, but - in this case, you need to have exactly one translation unit (e.g. compiled .cpp file) defining your member. Otherwise, the C++ linker sees one definition of Foo::A in parent.o and a second definition in child.o, and those conflict.

Thus,

Solution 1: Move the definition to another file

  1. Create a Foo.cpp, which includes Foo.hpp and defines Foo::A.
  2. Delete the definition from the header, so it isn't repeated in multiple locations

Solution 2: Make the definition conditional

This is not really recommended, but it does work.

  1. Surround the definition like so:

    #ifdef FOO_A_DEFINITION
    const int Foo::A = 100;
    #endif
  2. Create a Foo.cpp, which defines #FOO_A_DEFINITION and then includes Foo.hpp

This has the detriment of using macros, but the benefit of human users seeing the definition from the header.

Solution 3: Initialize in class definition

So, you are asking yourself "Why should they conflict? I want the compiler to know that they're the same variable!"

One way to do this to initialize the static member within the class body. Since this is a simple type, that's possible even with older versions of the language standard. This is also @idmean's suggested solution:

class Foo {
public:
static const int A = 100;
};

There's a caveat here, which is that you're "not really" defining the static member this way. You can use it, but not in every way you would use a variable defined with solutions (1.) or (2.). See discussion here:

See a discussion of this point here:

Defining static const integer members in class definition

Solution 4: Inline your static member

This solution is another way to tell the compiler "it's just one definition for everybody", which only works in C++17 or later:

#include <iostream>
class Foo {
public:
inline static const int A = 100;
};

This looks very similar to Solution 3, but actually does something very different!

With this solution, you're actually defining the static member multiple times - in each translation unit - but are telling the compiler to only keep a single definition when linking and encountering many of them.

Solutions (1.) and (2.) behave differently from this solution (4.) when you use dynamically-linked libraries. See:

Where to initialize static const member in c++17 or newer?

for an explanation.

Why should I not initialize static variable in header?

If you do it in the header, you'll get multiple definition errors as soon as you include it from more than one CPP file. You're really telling the compiler two things when you declare

int BaseClass::x = 10;

First, you're defining the symbol BaseClass::x; second you're telling it you want it to have the initial value of 10. According to the One Definition Rule this can only happen once in your program.

How to declare and initialize a static member in a class?

One can definitely have class static members which are not CV-qualified (non const and not volatile). It is just that one should not initialize them (give them value) when one declare them inside the class, based on current ISO C++ regulations. In Comparison, it is OK to do so for non static data members(regardless of CV-qualification) Since C++11.

Because static data members do not belong to any object, with the right access they can be assigned (and if they are not constant, they can be manipulated) outside of the class(keeping in mind the right scope operator). Also regardless of public/private declaration and CV-qualification, static data members can be initialized outside their class.

So one way for initializing static data members, is to do so in the same block-scope/namespace where their classes(outer class in case of sub-classes) are situated, but not inside any class scope.

For example:

class Graph {
public:
class Node {
public:
static int maxNumberOfNeighbors;
.
.
.
};
.
.
.
};

int Graph::Node::maxNumberOfNeighbors = 4;
//also int Graph::Node::maxNumberOfNeighbors(4);

Good luck!

How to have static data members in a header-only library?

C++17 and above

Use inline static variables for non-dynamic initialization:

struct Foo
{
inline static int I = 0;
};

And use function local static variables otherwise:

struct Foo
{
static std::string& Bar()
{
static std::string S = compute();
return S;
}
};

C++14 and below

Use function local statics, as they are plain easier to use.

If for some reason you really wish for a static data member, then you can use the template trick:

template <typename T = void>
struct Foo
{
static int I = 0; // inline initialization only for simple types.
};

template <typename T>
int Foo<T>::I;

On local statics

For resources which require dynamic initialization, it is best to use a local static.

The order in which file-scope or class-scope statics are dynamically initialized is undefined, in general, leading to the Static Initialization Order Fiasco when you try to read a uninitialized static as part of the initialization of another. Local static solve the issue by being initialized lazily, on first use.

There is some slight overhead to using local statics, however. From C++11 onwards, the initialization is required to be thread-safe, which typically means that any access is gated by an atomic read and well-predicted branch.

Static data member initialization in the class definition?

why (for what?) it is impossible to initialize the static variable inside the class?

It is not impossible to initialise static variables inside the class definition, if they are const.

But actually without std::size_t const s::len - this line it's compiles successfully too, so in what cases it shouldn't work?

It works because the variable was not odr-used. See [basic.def.odr]:

A variable x whose name appears as a potentially-evaluated expression e is odr-used by e unless

  • x is a reference ...
  • x is a variable of non-reference type that is usable in constant expressions and has no mutable subobjects, and e is an element of the set of potential results of an expression of non-volatile-qualified non-class type to which the lvalue-to-rvalue conversion ([conv.lval]) is applied, or ...

Besides, ODR violations are no diagnostic required. This is technically true for missing definitions. An optimisation might remove odr-uses so that they don't manifest as errors.

Try the following to odr-use the variable:

const int *ptr = &X::n;
  1. Why const static data members may be defined in the class definition?

No. Non-inline const static data members are not defined by the declaration in the class definition. There needs to be a definition (outside the class) if the variable is odr-used.


  1. Why static data members [...] may not be defined in the class definition (see my thoughts about template classes with static members and Stroustrup words about this (does he cheat us?))?

Stroustrup is correct about "complicated linker rules". Those were avoided when it was not possible to define static variables in headers.

Your point about templates makes sense. Those complicated linker rules were needed anyway to make templated static variables possible. And being able to define even non-templated static variables has advantages. These are probably the reasons why the committee chose to allow defining static members in class definition in C++17. See: inline static member definition.



Related Topics



Leave a reply



Submit