Marshall P. Cline 的 C++ FAQs (2nd Edition) (Paperback) 第 16.4 节,Greg Lomow说内联函数不能安全地访问静态数据成员,因为可以在初始化静态数据成员之前调用该函数。
我不明白为什么这适用于内联函数,而不仅仅是其他翻译单元中调用另一个翻译单元中的静态数据成员的任何函数?我看不出“内联”在这场灾难中扮演什么角色?
Marshall P. Cline 的 C++ FAQs (2nd Edition) (Paperback) 第 16.4 节,Greg Lomow说内联函数不能安全地访问静态数据成员,因为可以在初始化静态数据成员之前调用该函数。
我不明白为什么这适用于内联函数,而不仅仅是其他翻译单元中调用另一个翻译单元中的静态数据成员的任何函数?我看不出“内联”在这场灾难中扮演什么角色?
static
在执行同一翻译单元(或多或少的 cpp 文件)中的任何函数之前,变量已完全初始化。如果它们在不同的翻译单元中, 则不能保证它们在main
被调用之前被初始化。功能是重复的,每个翻译单元都有自己的副本。这意味着与变量不同的翻译单元中的内联函数可能会在正确初始化之前尝试读取/写入该变量,从而导致未定义的行为。(规则很复杂,但我记得是这样的)main
inline
static
§ 3.6.2/4 具有静态存储持续时间的非局部变量的动态初始化是否在 main 的第一条语句之前完成是实现定义的。如果初始化延迟到 main 的第一个语句之后的某个时间点,它应该发生在与要初始化的变量在同一翻译单元中定义的任何函数或变量的第一次 odr-use (3.2) 之前。
和
§ 3.2/3 内联函数应在使用 odr 的每个翻译单元中定义。
据我所知,内联函数并不比非内联函数更危险。任何访问不同 TU 中的静态函数的函数都是有风险的,并且由于inline
恰好将函数放在每个TU 中,因此它们中的大多数都不安全。一种解决方法是使用"construct on first use idiom"。
隐式模板特化很复杂,但为了完整性:
§ 14.7.1/3 [temp.inst] 静态数据成员的初始化(以及任何相关的副作用)不会发生,除非静态数据成员本身以需要定义静态数据成员的方式使用存在。
所以模板类的静态成员总是在使用前被初始化。
以上均受制于 静态初始化顺序惨败),由上述“首次使用 idom 构造”解决。