简体   繁体   English

头文件中的const变量和静态初始化fiasco

[英]const variables in header file and static initialization fiasco

After reading a lot of the questions regarding initialization of static variables I am still not sure how this applies to const variables at namespace level. 在阅读了很多关于静态变量初始化的问题之后,我仍然不确定这如何适用于命名空间级别的const变量。

I have kind of the following code in a header file config.h generated 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 static keyword 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 header file 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 const variables having internal linkage there should be no problem in both cases? 如果我理解正确,由于const变量具有内部联系,两种情况都应该没有问题?

Edit: (due to dribeas answer) 编辑:(由于运动答案)

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 path2 in this case? 关于这个用例的另一个问题:在这种情况下,编译器会优化远离path2吗?

Your first definition places path1 in each compilation unit that includes config.h . 您的第一个定义将path1放在包含config.h每个编译单元config.h To avoid this, don't define variables in header files. 为避免这种情况,请不要在头文件中定义变量。 Usually you'd declare the variables in the header as extern : 通常你会将标题中的变量声明为extern

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

and define them in a translation unit, eg 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";

static is not deprecated any more. static不再被弃用。 static and extern are sometimes implied, but I always forget where and how, so I usually specify them explicitly for all namespace-level variables. staticextern有时是暗示的,但我总是忘记在哪里以及如何,所以我通常为所有命名空间级变量显式指定它们。

I tried to get the necessary information right from the C++03 Standard document. 我试图从C ++ 03标准文档中获取必要的信息。 Here is what I found: 这是我发现的:

Regarding the const static declarations: 关于const static声明:

According to section 3.5.3 objects defined at namespace level and declared const have internal linkage by default. 根据3.5.3节在命名空间级别定义的对象和声明的const默认具有内部链接 static also declares a namespace level object to have internal linkage so there is no need to declare an object static 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: 关于静态初始化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 passing the 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 if dynamic initialization is done before the first statement of main . 如果main的第一个语句之前完成动态初始化,则在3.6.2.1和3.6.2.3中都没有指定不同编译单元中的静态对象的初始化顺序。

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.cpp has its own copy of the const variables, these might not be initialized when MyClass::MyClass() is called. 由于myclass.cpp有自己的const变量副本,因此在调用MyClass::MyClass()时可能不会初始化这些变量。

So yes, const variables 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)的对象应在任何动态初始化发生之前初始化。

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 foo will be initialized before foobar , even if both are constant. 无法保证foo将在foobar之前初始化,即使两者都是常量。 That means that the program behavior is undefined and it could well print "foobar", "bar" or die. 这意味着程序行为是未定义的,它可以很好地打印“foobar”,“bar”或者死亡。

Static initialization fiasco refers to static variables that depend on each other. 静态初始化fiasco是指彼此依赖的静态变量。 Merely defining some static const variables will not be a source of problems. 仅仅定义一些static const变量不会成为问题的根源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM