C++:static on static member variable dependent initial with int vs struct

tot*_*ing 8 c++ static c++11

鉴于这是从一个静态成员变量初始化的静态成员变量另一个类中,非文字struct ii有时缺省初始化为0 333.这取决于编译或链接顺序.伪代码演示:

class StaticClass: // file 'ONE.cpp/.hpp'
    static int i = 2
    static struct ii { int a = 333 }

class ABC: // file 'abc.cpp'
    static int abc_i = StaticClass::i   // always 2
    static struct abc_ii = StaticClass::ii // sometimes 0, sometimes 333
Run Code Online (Sandbox Code Playgroud)

调用g++ -std=c++11 abc.cpp ONE.cpp && ./a.out结果i = 2 / ii = 0(gcc 4.8.1,与clang ++ 3.7相同; -Wall -Wextra从不抱怨).

但是调用g++ -std=c++11 ONE.cpp abc.cpp && ./a.out结果i = 2 / ii = 333 !

对于ONE.o abc.ovs abc.o ONE.o以及以某种方式连接文件时也会发生同样的情况:

cat ONE.cpp abc.cpp > X.cpp && g++ X.cpp && ./a.out VS cat abc.cpp ONE.cpp > Y.cpp && g++ Y.cpp && ./a.out

删除在单个文件中包含和移动代码,当此顺序存在时,默认初始化为0:

const OneI ABC::def_ii = StaticClass::ii; const OneI StaticClass::ii = OneI{333};

以及这个排序的一到333:

const OneI StaticClass::ii = OneI{333}; const OneI ABC::def_ii = StaticClass::ii;

为什么这会发生在两个独立的编译单元中呢?通过强制执行后者的排序,可以以某种方式避免这种情况吗?使用静态指针ABCStaticClass::ii安全(我不希望,虽然)?

完整的C++代码:


/* File: abc.cpp */

#include <iostream>
#include "ONE.hpp"

struct ABC {
  ABC();

  static const int def_i;
  static const OneI def_ii;
  void arg_i(const int &x) { std::cout << "i = " << x << " ";};
  void arg_ii(const OneI &x) { std::cout << "/ ii = " << x.a << " ";};

};

ABC::ABC() {
  arg_i(def_i);
  arg_ii(def_ii);
}

const int ABC::def_i = StaticClass::i;
const OneI ABC::def_ii = StaticClass::ii;

int main() {
  ABC a;
  std::cout << '\n';
}
/* End: abc.cpp */
Run Code Online (Sandbox Code Playgroud)
/* File: ONE.cpp */

#include <iostream>

#include "ONE.hpp"

const int StaticClass::i = 2;
const OneI StaticClass::ii = OneI{333};

/* End: ONE.cpp */
Run Code Online (Sandbox Code Playgroud)
/* File: ONE.hpp */

#include <iostream>

#ifndef One
#define One

struct OneI {
  OneI(int a_) : a(a_) { }
  int a;
};

struct StaticClass {
  const static int i;
  const static OneI ii;
};

#endif // One header guard

/* End: ONE.hpp */
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 5

恭喜!你遇到过静态初始化命令fiasco.

静态对象的初始化顺序未在多个转换单元中定义.

StaticClass::ii在...中定义ONE.cppABC::def_ii定义abc.cpp.因此StaticClass::ii可能会或可能不会在之前初始化ABC::def_ii.由于初始化ABC::def_ii使用值StaticClass::ii的值将取决于是否StaticClass::ii初始化.

定义了翻译单元的静态对象的初始化顺序.对象按其定义的顺序初始化.因此,在连接源文件时,会定义初始化顺序.但是,当您以错误的顺序连接文件时,定义的初始化顺序是错误的:

const OneI ABC::def_ii = StaticClass::ii; // StaticClass::ii wasn't initialized yet
const OneI StaticClass::ii = OneI{333};
Run Code Online (Sandbox Code Playgroud)

通过强制执行后者的排序,可以以某种方式避免这种情况吗?

最简单的解决方案是以正确的顺序在同一翻译单元中定义两个对象.更通用的解决方案是使用Construct On First Use Idiom初始化静态对象.

使用静态指针ABCStaticClass::ii安全(我不希望,虽然)?

只要在指定了指向对象的另一个转换单元中的静态对象的初始化期间没有使用指针的解除引用值,是的,用ABC::def_ii指针替换将是安全的.


StaticClass::ii在静态初始化阶段††期间将进行零初始化.静态初始化命令fiasco涉及动态初始化阶段††.

†† C++标准草案[basic.start.static]

如果未执行常量初始化,则具有静态存储持续时间([basic.stc.static])或线程存储持续时间([basic.stc.thread])的变量将进行零初始化([dcl.init]).零初始化和常量初始化一起称为静态初始化; 所有其他初始化是动态初始化.在进行任何动态初始化之前,应执行静态初始化.[注意:非局部变量的动态初始化在[basic.start.dynamic]中描述; 本地静态变量的描述在[stmt.dcl]中描述. - 结束说明]