如何防止 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();    
}

总结一下:

那么,在 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 初始化的对象需要守卫。