C++ 为什么在定义的编译和链接之前引用外部实例的程序

C++ Why program with extern instance referenced before defined compiles and links

本文关键字:引用 外部 程序 实例 为什么 定义 编译 C++ 链接      更新时间:2023-10-16

问题可以通过一个简单的例子来显示:

extern A<bool> a;
B b(a);
A<bool> a("a");

在头文件中:

template<typename T>
class A {
public:
A(const char * name) : m_name(name) {}
const char * name() const {
return m_name;
}
...
...
private:
const char * m_name;
};
class B {
public:
B(A<bool> & a) {
printf("%s = %pn", a.name(), &a);
}
};

代码实际编译、链接和生成:

(null) = 0044F604

演示

我想知道这是否不应该被编译器捕获并失败。

有问题的编译器是gcc 9.2.0 (mingw-w64-i686)

在C++中,同一翻译单元中的全局对象按其定义的顺序进行初始化(尽管TU的顺序未定义(。参见 [basic.start.init]/2:

。在单个翻译单元中定义的有序初始化变量应按照其在翻译单元中的定义顺序进行初始化。

另一方面,静态存储持续时间的对象的存储是在程序加载时分配的,因此每个全局对象从一开始就有一个地址。

通常,在C++中,允许引用尚未初始化的对象,但访问它是未定义的行为。通常不需要对未定义的行为进行诊断。

在您的情况下,可以使用对A的引用构造B,并且可以保存传入的引用以供以后使用:

class B {
A<bool> & a_;
public:
B(A<bool> & a) : a_(a) { }
void run() {
printf("%s = %pn", a_.name(), &a_);
}
};
  1. 传递和使用对未初始化对象的引用并没有错。标准明确允许这样做。
  2. 程序员有责任以标准允许的有限方式之一使用此类引用。如果使用情况超出允许的范围,编译器不需要抱怨。相反,执行此操作的程序的行为是未定义的。

有关详细信息,请参阅 basic.life/6 和 basic.life/7。