编译器是否拥有生成所有全局变量的依赖树并为它们创建定义良好且正确的初始化顺序所需的所有信息?我意识到您可以使用全局变量编写循环依赖项-仅在这种情况下进行未定义的行为-编译器可能会发出警告,也可能会出错。
通常这种事情的原因是它会给编译器制造商带来负担或导致编译显着变慢。在这种情况下,我没有任何指标或证据表明其中任何一个都不正确,但我的倾向是两者都不正确。
编译器是否拥有生成所有全局变量的依赖树并为它们创建定义良好且正确的初始化顺序所需的所有信息?我意识到您可以使用全局变量编写循环依赖项-仅在这种情况下进行未定义的行为-编译器可能会发出警告,也可能会出错。
通常这种事情的原因是它会给编译器制造商带来负担或导致编译显着变慢。在这种情况下,我没有任何指标或证据表明其中任何一个都不正确,但我的倾向是两者都不正确。
嗯,想象一下下面的设置,它是完全有效的 C++,但很难分析:
// TU #1
bool c = coin();
// TU #2
extern bool c;
extern int b;
int a = c ? b : 10;
// TU #3
extern bool c;
extern int a;
int b = c ? 20 : a;
很明显 TU #1 需要先初始化,但是然后呢?使用静态引用的标准解决方案允许您使用标准 C++ 正确编写此代码,但通过修复全局初始化顺序来解决此问题似乎很棘手。
编译器可以处理的部分实际上是定义:具有静态存储持续时间的对象是按照它们的定义出现在翻译单元中的顺序构造的。销毁顺序正好相反。
在翻译单元之间对对象进行排序时,对象的依赖组通常没有明确表示。但是,即使明确表示了依赖关系,它们实际上也没有多大帮助:在小型项目中,具有静态存储持续时间的对象之间的依赖关系可以相对容易地管理。事情变得有趣的地方是大对象,但这些对象更有可能包含表单的初始化
static T global = functionWhichMayuseTheword();
即,在排序有用的情况下,它一定不起作用。
有一种简单的方法可以确保及时构造对象,这在 C++ 中甚至是线程安全的(在 C++03 中它不是线程安全的,因为该标准首先没有提到任何线程的概念) :使用函数本地static
对象并返回对它的引用。对象将根据需要构建,但如果它们之间存在依赖关系,这通常是可以接受的:
static T& global() {
static rc = someInitialization();
return rc;
}
鉴于有一个简单的解决方法,而且无论是提议还是工作实现都没有证明提议确实有效,因此几乎没有兴趣改变全局对象的初始化状态。更不用说改进对全局对象的支持似乎与改进一样有用goto
。
我不是编译器作者,所以请对我所说的话持保留态度。我认为原因如下。
1)希望保留单独编译的C模型。链接时间分析当然是允许的,但我怀疑他们不想让它成为必需。
2) Meyers Singleton(尤其是现在它已成为线程安全的)提供了一个足够好的替代方案,因为它几乎与全局变量一样易于使用,但提供了您正在寻找的保证。