How to prevent GCC from generating guards for static members

Viewed 434

Following is an minimal example that sometimes generates guards and sometimes not:

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();    
}

To summarize:

  • without ctor in class A, guards are never generated
  • using anaon-ns prevents guards also
  • making the static member m a static local in foo() still generates guards (with -fno-threadsafe-statics) (comment/uncomment the appropriate lines in example above)

So, how to grevent guards from being generated in the case class A has a ctor and using anon-ns is not possible?

3 Answers

The key-feature to suppress guards are constinit and constexpr ctors:

#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();    
}

With constinit the initialization must be performed on compile-time, so there is no need to generate guards. This requires a constexpr ctor. In the above example the ctor (at least for gcc) could be declared without constexpr, but this may be a pending bug.

You can declare the constructor of A as constexpr so that X<A>::m is statically initialized.

If the variable need to be dynamically initialized, then a guard has to be used to prevent multiple initialization.

Per 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.

inline variables are initialized by each translation unit that includes its definition. Without that guard each translation unit would re-initialize the very same variable.

constexpr constructors, obviously, make the initialization static, so that it doesn't need run-time initialization. But objects that do use dynamic initialization require the guard.

Related