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

C++ 头文件中的常量变量和静态初始化失败,c++,initialization,linkage,C++,Initialization,Linkage,在阅读了大量关于静态变量初始化的问题后,我仍然不确定这如何应用于命名空间级别的const变量 我在构建脚本生成的头文件config.h中有以下代码: static const std::string path1 = "/xyz/abc"; static const std::string path2 = "/etc"; 根据我所读到的内容,static关键字是不必要的,甚至在这里也不推荐使用 我的问题是:上面的代码容易出现静态初始化失败吗 如果头文件myclass.h中包含以下内容: clas

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

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

static const std::string path1 = "/xyz/abc";
static const std::string path2 = "/etc";
根据我所读到的内容,
static
关键字是不必要的,甚至在这里也不推荐使用

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

如果头文件
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");
这会给静态初始化带来任何问题吗

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

编辑:(由于Dribea的回答)

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

main.cpp
中:

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

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

int main()
{
    ...
}
const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");
#包括
#包括
std::string另一个字符串(path1+myclass1.Get());
int main()
{
...
}

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

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

extern const std::string path1;
extern const MyClass myclass1;
并在翻译单元中定义它们,例如
config.cpp

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

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

int main()
{
    ...
}
const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");
有时,您需要一个常量变量,该变量只能从一个转换单元中使用。然后,您可以在文件范围中将该变量声明为
static

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

static
不再被弃用<代码>静态和
外部
有时是隐含的,但我总是忘记在哪里以及如何,因此,我通常为所有名称空间级别变量显式指定它们。

当一个名称空间级别变量依赖于分配给不同名称空间级别变量的值时,静态初始化失败是一个问题,该值以前可能会初始化,也可能不会初始化。在您的两个示例中,没有这种依赖关系,也不应该有任何问题

另一方面,这很容易出现此类错误:

// 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;
}
//header.h
外部常量std::字符串foo;
//常数.cpp
常量std::字符串foo(“foo”);
//main.cpp
#包括“header.h”
常量std::字符串foobar(foo+“bar”);
int main(){

std::cout静态初始化失败指的是相互依赖的静态变量。仅定义一些
静态常量
变量不会导致问题。

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

关于
const static
声明:

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

同样根据附件D.2

static关键字的用法是 在中声明对象时已弃用 名称空间范围(见3.3.5)

关于静态初始化失败:

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

根据第3.6.2.1节:

具有静态存储持续时间的对象 在相同的命名空间范围中定义 翻译单元与动态翻译 初始化应在中初始化 它们定义的顺序 显示在翻译单元中

答案1:这意味着将变量传递给静态对象构造函数应该可以

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

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

考虑以下几点:

// 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"
}
//consts.h
#包括
const std::string string1=“ham”;
const std::string string2=“cheese”;
//myclass.h
#包括
类MyClass
{
公众:
MyClass();
MyClass(std::string str);
std::string Get(){return memberString;}
私人:
std::字符串成员字符串;
}
//myclass.cpp
#包括“常数h”
#包括“myclass.h”
MyClass::MyClass():memberString(string1){}
MyClass::MyClass(std::string str):memberString(str){}
//main.cpp
#包括
#包括“常数h”
#包括“myclass.h”
MyClass myObject1;
MyClass myObject2(string2);
使用名称空间std;
int main()
{

我是否可以在原始问题中更新我的用例。这正是我想知道的场景。我不敢对此发表评论。我的第一个想法是它应该是好的(常量是翻译单元的局部变量,在第二个常量之前定义),但如果可能的话,我会避免这种构造。我知道最简单的确定方法是在内部使用带有静态变量的函数,而不是全局变量:
inline myclass&global_object(){myclass instance;return instance;}
该语言保证
实例
将在函数的第一次调用中完全初始化,因此
std::string other(global_object().Get())
肯定会起作用……无论如何,我真的会尝试完全避免它,并在初始化时消除名称空间级别的变量。名称空间级别变量的初始化并不简单,它发生在两个过程中,所有从常量实例化的全局变量都会首先获得值,然后在第二个过程中获得值依赖于非常量的东西将根据定义顺序初始化(在同一翻译单元内),