一般说静态对象的析构函数是以与构造函数相反的顺序调用的。据我了解,constinit 对象是在编译时初始化的,因此它们的析构函数应该在“普通”静态对象的析构函数之后调用。
该程序
struct A
{
constexpr A(const char* t): t_(t) {}
~A() {std::cout << "~A(" << t_ << ")\n";}
const char* t_;
};
static A a1("static");
int main () {
static constinit A a2("constinit");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(使用 GCC 10),但是,给出输出
~A(constinit)
~A(static)
Run Code Online (Sandbox Code Playgroud)
即 constinit 对象在“正常”静态对象之前被销毁(尽管它是更早构造的)。“逆序”规则对 constinit 对象不再有效吗?
下面的代码在同一个翻译单元中,并且是A::v在之后定义的x,为什么A::v没有初始化为“ok”?
#include <string>
#include <iostream>
std::string foo() {
return "OK";
}
std::string x = foo();
struct A {
static inline std::string v = x;
};
int main() {
std::cout << A::v << std::endl; // didn't print "OK", why?
}
Run Code Online (Sandbox Code Playgroud) c++ static-members initialization-order language-lawyer c++17
C++ 标准(至少早于 C++17)已经说明了初始化顺序。
在同一翻译单元的命名空间范围内定义并动态初始化的静态存储持续时间的对象应按照其定义在翻译单元中出现的顺序进行初始化。
C++17 引入了内联变量,我相信这意味着可以在多个翻译单元中定义具有静态存储持续时间和命名空间范围以及动态初始化的单个变量。
C++ 是否对这些变量的初始化顺序做出任何保证?
假设我有一些类型类
trait FooBar[X]
Run Code Online (Sandbox Code Playgroud)
和一个实例FooBar[Int]:
given intIsFooBar: FooBar[Int] = new FooBar {}
Run Code Online (Sandbox Code Playgroud)
现在,假设我有一个Intf具有某种成员类型的接口A,并且还保证有一个given FooBar[A]:
trait Intf:
type A
given aIsFoobar: FooBar[A]
Run Code Online (Sandbox Code Playgroud)
现在,我有了类型Int,也有一个FooBar[Int],但是我如何实际实现这个接口呢Int?
如果我尝试
class IntImpl() extends Intf:
type A = Int
given aIsFoobar: FooBar[A] = summon
Run Code Online (Sandbox Code Playgroud)
然后我收到“函数体 IntImpl.aIsFoobar 中的无限循环”错误,因为summon似乎看到的是aIsFoobar而不是intIsFooBar。
如果我尝试使用summon某些辅助辅助变量中的实例,如下所示:
class IntImpl() extends Intf:
type A = Int
private final val _aIsFoobar: FooBar[A] = summon
given aIsFoobar: …Run Code Online (Sandbox Code Playgroud) 有一个简单且众所周知的模式可以避免静态初始化失败,在C++ FAQ Lite 的第 10.13 节中进行了描述。
在这个标准模式中,有一个权衡是构造的对象永远不会被破坏(如果析构函数没有重要的副作用,这不是问题)或者不能从另一个静态对象的析构函数安全地访问静态对象(请参阅C++ FAQ Lite 的 10.14 节)。
所以我的问题是:如果静态对象的析构函数具有最终必须发生的重要副作用并且静态对象必须由另一个静态对象的析构函数访问,那么您如何避免静态反初始化失败?
(注意:FAQ-lite 提到这个问题在C++ FAQs: FAQs 16.17 of C++ FAQs: FAQs by M. Cline and and and G. Lomow 中得到了回答。我无权阅读这本书,这就是我问这个问题的原因。 )
鉴于代码示例:
class B {
//Some contents.
};
class C {
private:
B& b;
};
class A {
private:
B b;
C c;
};
Run Code Online (Sandbox Code Playgroud)
C类引用了ab,因此需要用它进行初始化.A类包含B的实例和C的实例.
我的问题是:我可以使用A中的B实例初始化A中的C实例(假设我没有把构造函数放入其中)?其次,我是否需要在A中执行B的任何显式初始化,还是默认初始化,因为它是类中的类类型?
在C++中未定义自由对象的初始化顺序.但是下面呢?
namespace foo {
char const* str = "hey";
struct A {
A() { cout << str; }
} obj;
}
Run Code Online (Sandbox Code Playgroud)
这仍然是未定义的行为,还是对使用字符串文字初始化的指针有特殊规定?
除此之外:如果str是"char const []"类型怎么办?如果它是一个std :: string?