微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

'cppcoreguidelines-interfaces-global-init' 仅在特定情况下出错

如何解决'cppcoreguidelines-interfaces-global-init' 仅在特定情况下出错

鉴于cppcoreguidelines-interfaces-global-init,特别是“根据未初始化的非局部变量用非常量表达式初始化非局部变量”,即以here为例,我有以下场景:

  • 我的团队由 4 名开发人员组成
  • 我们都有相同的环境:VS2015

    enter image description here

  • 每个人都有相同的 VS 项目选项
  • 我们的硬件略有不同。

然后,我发现了一个类似下面的本地 static,上面的警告以错误的初始化结束。

static int GlobalScopeBadInit1 = ExternGlobal;

到目前为止,一切都很好 - 这是一个糟糕的初始化,可能会出错,我们需要修复它。

问题是:为什么我的机器会出错?无论我们多么努力——调试或发布,它都会发生在我的机器上。我们已经清理并删除其他开发人员机器上的文件,上面的代码在我的机器上出错率为 100%,而在其他开发人员的机器上出错率为 0%。 它也不会发生在构建机器上。

有人知道什么可以解释这种行为吗?

谢谢。

解决方法

当一个翻译单元中的对象可能依赖来自另一个尚未初始化或正在初始化的单元的数据时,就会发生static initialization order fiasco,您正在遭受这种痛苦。

> 跨翻译单元的 static 个成员的

Dynamic initialization 相对于所有其他翻译单元不确定地排序。无法保证它们被初始化的顺序,或者即使它们在同一个线程上。

我能想到的几种可能情况可能会导致您遇到它,而其他情况则不会。硬件、链接顺序、时序等因素都可能起作用。一些可能的情况:

  1. 由于动态初始化是不确定的,甚至不能保证存在于同一个线程中,如果在启动时是多线程的,那么不同的硬件可能会引入不同的初始化时间(竞争条件)

  2. 可能只是您系统上的开发环境有所不同。在不同位置找到库这样的小事可能会破坏库加载顺序,这可能会影响初始化的顺序。

  3. 假设您的系统正在执行并行编译,则您的硬件可能正在以不同的顺序编译对象,这会影响这些对象的链接顺序。链接顺序可能会更改初始化顺序——这可能会导致您遇到其他人没有遇到的问题。

最终,为什么您会遇到这种情况,而其他人则不会。实际上并不重要。正式地说,您正在经历未定义的行为,它的行为方式以及它歧视的对象无法解释


注意:如果没有关于您的系统设置的更多信息,只能猜测为什么会发生这种情况。

,

您面临的问题是由 static initialization order fiasco 引起的,简而言之,未指定跨不同编译单元的静态和全局变量的构造和销毁顺序。

在您的情况下,问题仅在特定环境中而不是在另一个环境中发生(即初始化顺序导致问题)这一事实正是未指定顺序的意思:它受编译顺序,它可能会受到编译器优化和其他可能与环境相关的考虑因素的影响。它甚至可以在不同的线程中同时初始化(请参阅:C++ spec [stmt.dcl] 以及可能发生的原因和时间at the proper section in the original working doc dealing with that issue)。

静态初始化顺序失败有解决方案吗?

是的,有几种可能的解决方案。


第一个解决方案可能是重新设计

重新设计选项 1 - 更改代码,使您不会拥有多个全局对象。

您可以在单个实际全局对象中处理所有其他“全局对象”。有一些库以这种方式处理单例,在单个全局 SingletonManager 中管理所有单例。但由于这样的更改可能需要大量的代码更改,伴随着风险,您可能需要考虑其他选项。

重新设计选项 2 - 使用静态或全局函数而不是全局变量 - 一旦从静态或全局函数中检索到全局变量,如下所示,初始化顺序就解决了:

Boo& get_global_boo() {
    static Boo b(get_global_foo());
    return b;
}

Foo& get_global_foo() {
    static Foo f(42);
    return f;
}

您可以将 this code example facing the initialization order fiascoone which solves the issue with static methods 进行比较。这种方法有时被称为“Meyers Singleton”,代表 Scott Meyers,他在他的著作“更有效的 C++” 中讨论了这种方法。

有关该方法的更多信息,请参见 herehere

重新设计选项 3 - 更简单的重新设计方法是将所有全局变量移动到单个编译单元。这种方法需要对代码进行较少的更改,而且可能风险较小。但并非总是可行。


另一种解决方案是使用特定于编译器的选项来设置初始化顺序 - 可以使用 init_seg 在 Visual Studio 中手动管理静态和全局初始化的顺序 - Visual Studio特定的 pragma 允许开发人员控制初始化的顺序。请参阅:MSDN documentation 和此 blog post

GCC 也有自己的用于此目的的属性 - init_priority


最后一个选项是最好的,但也是最复杂的 - 您可以按照 C++ 库对 std::cout 的技巧进行操作,std::cout 是一个全局对象,但是 保证在您需要时初始化,即使在全局上下文中也是如此。这是通过漂亮的计数器成语完成的。您可以阅读有关此习语 in the C++ Idioms wiki 以及此 SO question 的更多信息。请注意,漂亮的计数器习语并不是针对全局上下文中 static std::ios_base::Init force_init; 使用的所有情况的解决方案,并且在极少数情况下,需要使用如下语句“帮助”它正确工作:as请参阅this SO post


有关该问题的其他讨论,另请参阅:Static variables initialisation order

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。