如何防止 GCC 为静态成员生成守卫
How to prevent GCC from generating guards for static members
以下是一个有时会生成守卫有时不会生成守卫的最小示例:
struct A {
inline A(int v = 0) {} // without ctors, guards are omitted
int m1() const {
return m;
}
private:
int m = 0;
};
//namespace { // without anon-ns guards are generated
template<typename T>
struct X {
static int foo() {
// static T m; // even as local-static and -fno-threadsafe-statics, guards are generated
return m.m1();
}
inline static T m; // comment this and uncomment above to try as local-static
};
//}
int main() {
return X<A>::foo();
}
总结一下:
- classA 中没有 ctor,永远不会生成守卫
- 使用 anaon-ns 也可以防止守卫
- 使静态成员
m
成为 foo()
中的静态局部变量仍然会生成守卫(使用 -fno-threadsafe-statics
)(comment/uncomment 上面示例中的适当行)
那么,在 class A
有一个 ctor 并且无法使用 anon-ns 的情况下,如何避免生成守卫?
您可以将 A
的构造函数声明为 constexpr
,以便 X<A>::m
被静态初始化。
如果变量需要动态初始化,那么就必须使用守卫来防止多次初始化。
根据 Itanium C++ ABI:
If a function-scope static variable or a static data member with vague linkage (i.e., a static data member of a class template) is dynamically initialized, then there is an associated guard variable which is used to guarantee that construction occurs only once.
抑制守卫的关键特征是 constinit 和 constexpr 构造函数:
#include <cstdint>
struct A {
inline constexpr A(uint8_t v) : m{v} {} // without constexpr it should not compile, but does anymay
auto m1() const {
return m;
}
private:
uint8_t m{0};
};
template<typename T>
struct X {
static auto foo() {
return m.m1();
}
constinit inline static T m{2}; // requires constexpr ctor
};
int main() {
return X<A>::foo();
}
使用constinit
初始化必须在编译时执行,所以不需要生成守卫。这需要一个 constexpr
构造函数。在上面的示例中,可以在没有 constexpr
的情况下声明 ctor(至少对于 gcc),但这可能是一个未决的错误。
inline
变量由包含其定义的每个翻译单元初始化。如果没有那个守卫,每个翻译单元都会重新初始化同一个变量。
constexpr
构造函数,显然,使初始化 静态 ,因此它不需要 运行 次初始化。但是使用 dynamic 初始化的对象需要守卫。
以下是一个有时会生成守卫有时不会生成守卫的最小示例:
struct A {
inline A(int v = 0) {} // without ctors, guards are omitted
int m1() const {
return m;
}
private:
int m = 0;
};
//namespace { // without anon-ns guards are generated
template<typename T>
struct X {
static int foo() {
// static T m; // even as local-static and -fno-threadsafe-statics, guards are generated
return m.m1();
}
inline static T m; // comment this and uncomment above to try as local-static
};
//}
int main() {
return X<A>::foo();
}
总结一下:
- classA 中没有 ctor,永远不会生成守卫
- 使用 anaon-ns 也可以防止守卫
- 使静态成员
m
成为foo()
中的静态局部变量仍然会生成守卫(使用-fno-threadsafe-statics
)(comment/uncomment 上面示例中的适当行)
那么,在 class A
有一个 ctor 并且无法使用 anon-ns 的情况下,如何避免生成守卫?
您可以将 A
的构造函数声明为 constexpr
,以便 X<A>::m
被静态初始化。
如果变量需要动态初始化,那么就必须使用守卫来防止多次初始化。
根据 Itanium C++ ABI:
If a function-scope static variable or a static data member with vague linkage (i.e., a static data member of a class template) is dynamically initialized, then there is an associated guard variable which is used to guarantee that construction occurs only once.
抑制守卫的关键特征是 constinit 和 constexpr 构造函数:
#include <cstdint>
struct A {
inline constexpr A(uint8_t v) : m{v} {} // without constexpr it should not compile, but does anymay
auto m1() const {
return m;
}
private:
uint8_t m{0};
};
template<typename T>
struct X {
static auto foo() {
return m.m1();
}
constinit inline static T m{2}; // requires constexpr ctor
};
int main() {
return X<A>::foo();
}
使用constinit
初始化必须在编译时执行,所以不需要生成守卫。这需要一个 constexpr
构造函数。在上面的示例中,可以在没有 constexpr
的情况下声明 ctor(至少对于 gcc),但这可能是一个未决的错误。
inline
变量由包含其定义的每个翻译单元初始化。如果没有那个守卫,每个翻译单元都会重新初始化同一个变量。
constexpr
构造函数,显然,使初始化 静态 ,因此它不需要 运行 次初始化。但是使用 dynamic 初始化的对象需要守卫。