C++ 头文件中的 const 变量和静态初始化失败

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4806510/
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-28 16:34:47  来源:igfitidea点击:

const variables in header file and static initialization fiasco

c++initializationlinkage

提问by Hanno S.

After reading a lot of the questions regarding initialization of static variables I am still not sure how this applies to constvariables at namespace level.

在阅读了很多关于静态变量初始化的问题后,我仍然不确定这如何应用于const命名空间级别的变量。

I have kind of the following code in a headerfile config.hgenerated by the build script:

我在构建脚本生成的文件中有以下代码config.h

static const std::string path1 = "/xyz/abc";
static const std::string path2 = "/etc";

According to what I have read the statickeyword is not necessary, even deprecated here.

根据我所读的static关键字是没有必要的,甚至在这里弃用。

My Question:Is the code above prone to the static initialization fiasco?

我的问题:上面的代码是否容易出现静态初始化失败?

If I have the following in a headerfile myclass.h:

如果我在文件中有以下内容myclass.h

class MyClass
{
public:
    MyClass(const std::string& str) : m_str(str) {}
    std::string Get() const { return m_str; }

private:
    std::string m_str;
}

const MyClass myclass1("test");

Will this pose any problems with static initialization?

这会对静态初始化造成任何问题吗?

If I understood right, due to constvariables having internal linkage there should be no problem in both cases?

如果我理解正确,由于const变量具有内部链接,这两种情况都应该没有问题?

Edit:(due to dribeas answer)

编辑:(由于 dribeas 的回答)

Maybe I should mention that I am interested in use cases like:

也许我应该提到我对以下用例感兴趣:

In main.cpp:

main.cpp

#include <config.h>
#include <myclass.h>

std::string anotherString(path1 + myclass1.Get());

int main()
{
    ...
}

Another question regarding this use case: Will the compiler optimize away path2in this case?

关于这个用例的另一个问题:path2在这种情况下编译器会优化吗?

采纳答案by Hanno S.

I tried to get the necessary information right from the C++03 Standard document. Here is what I found:

我试图从 C++03 标准文档中获取必要的信息。这是我发现的:

Regarding the const staticdeclarations:

关于const static声明:

According to section 3.5.3 objects defined at namespace level and declared consthave internal linkageby default. staticalso declares a namespace level object to have internal linkage so there is no need to declare an object static const.

根据第 3.5.3 节,在命名空间级别定义并声明的对象默认const具有内部链接static还声明了具有内部链接的命名空间级对象,因此无需声明对象static const

Also according to Annex D.2

也根据附件 D.2

The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5).

在命名空间范围内声明对象时,不推荐使用 static 关键字(参见 3.3.5)。

Regarding the static initialization fiasco:

关于静态初始化失败:

Since the variables are defined in a header file they are always defined before any other static objects using them.

由于变量是在头文件中定义的,因此它们总是在使用它们的任何其他静态对象之前定义。

From section 3.6.2.1:

来自第 3.6.2.1 节:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

在同一个翻译单元的命名空间范围内定义并动态初始化的静态存储持续时间的对象应按照其定义在翻译单元中出现的顺序进行初始化。

Answer 1:This means passingthe variables to a static object constuctor should be fine.

答案 1:这意味着变量传递给静态对象构造函数应该没问题。

Answer 2:However a problem could occur if the variables are referenced from a non-inline constructor of a static object:

答案 2:但是,如果从静态对象的非内联构造函数引用变量,则可能会出现问题:

Neither in section 3.6.2.1 nor 3.6.2.3 is it specified in which order the static objects in different compilation units are initialized ifdynamic initialization is done before the first statement of main.

在 3.6.2.1 和 3.6.2.3 中都没有指定如果在 的第一条语句之前进行动态初始化,则不同编译单元中的静态对象的初始化顺序main

Consider the following:

考虑以下:

// consts.h
#include <string>

const std::string string1 = "ham";
const std::string string2 = "cheese";

// myclass.h
#include <string>

class MyClass
{
public:
    MyClass();
    MyClass(std::string str);
    std::string Get() { return memberString; }
private:
    std::string memberString;
}

// myclass.cpp
#include "consts.h"
#include "myclass.h"

MyClass::MyClass() : memberString(string1) {}

MyClass::MyClass(std::string str) : memberString(str) {}

// main.cpp
#include <iostream>
#include "consts.h"
#include "myclass.h"

MyClass myObject1;
MyClass myObject2(string2);

using namespace std;

int main()
{
    cout << myObject1.Get(); // might not print "ham"
    cout << myObject2.Get(); // will always print "cheese"
}

Since myclass.cpphas its own copy of the constvariables, these might not be initialized when MyClass::MyClass()is called.

由于myclass.cpp有自己的const变量副本,因此在MyClass::MyClass()调用时可能不会初始化这些变量。

So yes, constvariables defined in header files can be used in a way that is prone to the static initialization fiasco

所以是的,const在头文件中定义的变量可以以一种容易出现静态初始化失败的方式使用

As far as I can see this does only apply to variables not requiring static initialization:

据我所知,这仅适用于不需要静态初始化的变量:

From C++03 standard, section 3.6.2.1:

来自 C++03 标准,第 3.6.2.1 节:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

具有用常量表达式 (5.19) 初始化的静态存储持续时间的 POD 类型 (3.9) 的对象应在任何动态初始化发生之前进行初始化。

回答by Philipp

Your first definition places path1in each compilation unit that includes config.h. To avoid this, don't define variables in header files. Usually you'd declare the variables in the header as extern:

您的第一个定义path1位于每个包含config.h. 为了避免这种情况,不要在头文件中定义变量。通常,您会将标头中的变量声明为extern

extern const std::string path1;
extern const MyClass myclass1;

and define them in a translation unit, e.g. config.cpp:

并在翻译单元中定义它们,例如config.cpp

const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");

Sometimes you need a constant variable that is usable only from one translation unit. Then you can declare that variable at file scope as static.

有时您需要一个只能从一个翻译单元使用的常量变量。然后您可以在文件范围内将该变量声明为static.

static const std::string path1 = "/xyz/abc";

staticis not deprecated any more. staticand externare sometimes implied, but I always forget where and how, so I usually specify them explicitly for all namespace-level variables.

static不再被弃用。staticextern有时是隐含的,但我总是忘记在哪里以及如何,所以我通常为所有命名空间级变量明确指定它们。

回答by David Rodríguez - dribeas

What is referred as the static initialization fiasco is a problem when one namespace level variable depends on the value assigned to a different namespace level variable that might or not be initialized before. In your two examples there is no such dependency and there should not be any problem.

当一个命名空间级别变量取决于分配给不同命名空间级别变量的值时,静态初始化失败是一个问题,该变量之前可能已初始化或未初始化。在您的两个示例中,没有这种依赖性,应该没有任何问题。

This, on the other hand, is prone to that type of error:

另一方面,这很容易出现这种类型的错误:

// header.h
extern const std::string foo;

// constant.cpp
const std::string foo( "foo" );

// main.cpp
#include "header.h"
const std::string foobar( foo+"bar" );
int main() {
   std::cout << foobar << std::endl;
}

There is no guarantee that foowill be initialized before foobar, even if both are constant. That means that the program behavior is undefined and it could well print "foobar", "bar" or die.

即使两者都是常量,也不能保证foo会在 之前初始化foobar。这意味着程序行为是未定义的,它可以很好地打印“foobar”、“bar”或死亡。

回答by Puppy

Static initialization fiasco refers to static variables that depend oneach other. Merely defining some static constvariables will not be a source of problems.

静态初始化失败是指静态变量相互依赖。仅仅定义一些static const变量不会成为问题的根源。