How to initialize static members in the header

2019-02-02 03:06发布

Given is a class with a static member.

class BaseClass
{
public:
    static std::string bstring;
};

String has obviously to be default-initialized outside of the class.

std::string BaseClass::bstring {"."};

If I include the above line in the header along with the class, I get a symbol multiply defined error. It has to be defined in a separate cpp file, even with include guards or pragma once.

Isn't there a way to define it in the header?

7条回答
孤傲高冷的网名
2楼-- · 2019-02-02 03:25

If the initializer can be expressed as a literal, it is solved in C++11:

inline std::string operator"" _s(const char* p, size_t n) {
    return std::string{p, n};
}

class BaseClass {
    // inline initialization using user-defined literals
    // should allow for multiple definitions
    std::string bstring{"."_s};
};
查看更多
劳资没心,怎么记你
3楼-- · 2019-02-02 03:27

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.

查看更多
Summer. ? 凉城
4楼-- · 2019-02-02 03:29

UPDATE: My answer below explains why this cannot be done in the way suggested by the question. There are at least two answers circumventing this; they may or may not solve the problem.


The bstring static member has to be linked to a specific memory address. For this to happen, it has to appear in a single object file, therefore it has to appear in a single cpp file. Unless you're playing with #ifdef's to make sure this happens, what you want cannot be done in the header file, as your header file may be included by more than one cpp files.

查看更多
地球回转人心会变
5楼-- · 2019-02-02 03:30

To keep the definition of a static value with the declaration in C++11 a nested static structure can be used. In this case the static member is a structure and has to be defined in a .cpp file, but the values are in the header.

class BaseClass
{
public:
  static struct _Static {
     std::string bstring {"."};
  } global;
};

Instead of initializing individual members the whole static structure is initialized:

BaseClass::_Static BaseClass::global;

The values are accessed with

BaseClass::global.bstring;

Note that this solution still suffers from the problem of the order of initialization of the static variables. When a static value is used to initialize another static variable, the first may not be initialized, yet.

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

In this case the static variable headers will contain either { "" } or { ".h", ".hpp" }, depending on the order of initialization created by the linker.

查看更多
smile是对你的礼貌
6楼-- · 2019-02-02 03:33

No, it can't be done in a header - at least not if the header is included more than once in your source-files, which appears to be the case, or you wouldn't get an error like that. Just stick it in one of the .cpp files and be done with it.

查看更多
来,给爷笑一个
7楼-- · 2019-02-02 03:33

§3.2.6 and the following paragraphs from the current c++ 17 draft (n4296) define the rules when more than one definition can be present in different translation units:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then [...]

Obviously definitions of static data members of class type are not considered to appear in multiple translations units. Thus, according to the standard, it is not allowed.

The suggested answers from Cheers and hth. - Alf and Dietmar are more kind of a "hack", exploiting that definitions of

static data member of a class template (14.5.1.3)

and

inline function with external linkage (7.1.2)

are allowed in multiple TU ( FYI: static functions defined inside a class definition have external linkage and are implicitly defined as inline ) .

查看更多
登录 后发表回答