C++ 如何在标头中初始化静态成员

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18860895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:15:56  来源:igfitidea点击:

How to initialize static members in the header

c++classstatic-members

提问by Appleshell

Given is a class with a static member.

给定的是一个带有静态成员的类。

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

String has obviously to be default-initializedoutside 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 definederror. It has to be defined in a separate cppfile, even with include guardsor pragma once.

如果我将上面的行与类一起包含在标题中,则会出现symbol multiply defined错误。它必须在单独的cpp文件中定义,即使使用include guardspragma once

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

没有办法在标题中定义它吗?

回答by Dietmar Kühl

You can't define a staticmember 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.

您不能static多次定义成员变量。如果将变量定义放入标题中,它将在包含标题的每个翻译单元中定义。由于包含守卫只影响一个翻译单元的编译,它们也无济于事。

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

但是,您可以定义static成员函数!现在,乍一看,这似乎没什么帮助,当然,该函数可以具有局部static变量,并且返回对其中一个的引用的行为几乎类似于static成员变量:

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

The local staticvariable 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 staticvariable is thread-safe.

局部static变量将在第一次调用此函数时初始化。也就是说,构造被延迟到第一次访问该函数。当然,如果你使用这个函数来初始化其他全局对象,它也可以确保对象被及时构造。如果您使用多个线程,这可能看起来像是潜在的数据竞争,但事实并非如此(除非您使用 C++03):函数局部static变量的初始化是线程安全的。

回答by Cheers and hth. - Alf

Regarding

关于

Isn't there a way to define [the static data member] in the header?

不是有办法在header中定义【静态数据成员】吗?

Yes there is.

就在这里。

template< class Dummy >
struct BaseClass_statics
{
    static std::string bstring;
};

template< class Dummy >
std::string BaseClass_statics<Dummy>::bstring = ".";

class BaseClass
    : public BaseClass_statics<void>
{};

An alternative is to use a function, as Dietmar suggested. Essentially that is a Meyers' singleton (google it).

另一种方法是使用函数,正如 Dietmar 建议的那样。本质上这是一个迈耶斯的单身人士(谷歌它)。

Edit: Also, since this answer was posted we've got the inline object proposal, which I think is accepted for C++17.

编辑:另外,由于发布了这个答案,我们已经得到了内联对象提议,我认为它被 C++17 接受。

Anyway, think twiceabout the design here. Globals variables are Evil™. This is essentially a global.

无论如何,请三思这里的设计。全局变量是 Evil™。这本质上是一个全球性的。

回答by André von Kugland

In C++17 you can use inline variables, which you can use even outsideclasses.

在 C++17 中,您可以使用内联变量,甚至可以外部使用。

The inline specifier, when used in a decl-specifier-seq of a variable with static storage duration (static class member or namespace-scope variable), declares the variable to be an inline variable.

A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable.?1?

内联说明符在具有静态存储期的变量(静态类成员或命名空间范围变量)的声明说明符序列中使用时,将变量声明为内联变量。

声明为 constexpr 的静态成员变量(但不是命名空间范围的变量)隐式是内联变量。?1?

For example:

例如:

class Someclass {
public:
    inline static int someVar = 1;
};

Or,

或者,

namespace SomeNamespace {
    inline static int someVar = 1;
}

?1? https://en.cppreference.com/w/cpp/language/inline

?1? https://en.cppreference.com/w/cpp/language/inline

回答by Marko Mahni?

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.

为了使用 C++11 中的声明保持静态值的定义,可以使用嵌套的静态结构。在这种情况下,静态成员是一个结构,必须在 .cpp 文件中定义,但值在标题中。

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 headerswill contain either { "" } or { ".h", ".hpp" }, depending on the order of initialization created by the linker.

在这种情况下,静态变量将包含 { "" } 或 { ".h", ".hpp" },具体取决于链接器创建的初始化顺序。

回答by SebNag

§3.2.6and 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:

§3.2.6当前 c++ 17 草案 (n4296) 中的以下段落定义了不同翻译单元中可以存在多个定义时的规则:

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 [...]

类类型(第 9 条)、枚举类型(7.2)、具有外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数模板(14.5.6)可以有多个定义、类模板的静态数据成员 (14.5.1.3)、类模板的成员函数 (14.5.1.1) 或在程序中未指定某些模板参数的模板特化 (14.7, 14.5.5),前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求。给定在多个翻译单元中定义的名为 D 的实体,那么 [...]

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

来自 Cheers 和 hth 的建议答案。- 阿尔夫和迪特玛更像是一种“黑客”,利用了这些定义

static data member of a class template (14.5.1.3)

类模板的静态数据成员 (14.5.1.3)

and

inline function with external linkage (7.1.2)

带有外部链接的内联函数 (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 ) .

允许在多个 TU 中(仅供参考:在类定义中定义的静态函数具有外部链接并隐式定义为 inline )。

回答by Mats Petersson

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.

不,它不能在标题中完成 - 至少如果标题在您的源文件中包含不止一次,这似乎是这种情况,否则您不会得到这样的错误。只需将其粘贴在 .cpp 文件之一中即可完成。

回答by nickie

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 bstringstatic 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 cppfile. 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 cppfiles.

bstring静态成员已被链接到一个特定的内存地址。为此,它必须出现在单个目标文件中,因此它必须出现在单个cpp文件中。除非您使用#ifdef's 来确保发生这种情况,否则您无法在头文件中完成您想要的操作,因为您的头文件可能包含在多个cpp文件中。

回答by Elazar

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

如果初始值设定项可以表示为字面量,则在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};
};