The problem with static/global variables is their storage duration, not just their scope/visibility/linkage. In particular, it is impossible to predict the order in which multiple global variables with static storage duration are initialized and destroyed. I have thought multiple times that I'd be able to work around issues with static initialization/destruction, but a number of segfaults have convinced me otherwise.
The life cycle of a variable with static storage duration is the following:
- the variable begins existence when the program is loaded
- at some indeterminate point, initialization runs
- now, the variable can be used
- at some indeterminate point, the destructor is invoked
- the program terminates
All of these issues apply regardless of whether the variable is declared
static so that it is local to a compilation unit, though function-scoped static variables at least have predictable initialization (when the declaration is first executed).
The Google C++ style guide attempts to eliminate sources of possible errors. Then, the variable is legal almost during the entire run time of the program.
- If variables with static storage duration are used, they should be initialized with a constexpr so that the variable is initialized immediately. Dynamic initialization is only allowed if this initialization does not depend on the ordering relative to other variables. Dynamic initialization for function-scoped static variables is usually OK.
- The variables should use a trivial destructor (a destructor that does “nothing”) so that ordering is not a concern, and the variable effectively stays alive until the program terminates. Note also that a program might terminate without running these destructors, so you cannot rely on this destructor for correctness.
shared_ptr cannot be used in accordance with these requirements.
- Initialization is actually fine because the constructor is constexpr (the variable is initialized to a null pointer).
- However, a
shared_ptr is not trivially destructible so that there are ordering issues during the program's destruction phase.
A consequence of a
static shared_ptr<Foo> could be that a
Foo implementation has a destructor that runs at an unpredictable time, in particular at a time where other parts of your data are no longer alive. You mention that this shared pointer will only be used within that compilation unit, which means you could prevent such cases from happening and can audit any involved code with modest effort. In particular, if Foo's destructor is trivial, then the
shared_ptr might be safe in practice. But since the safety of this variable depends on lots of other code, it is error-prone. The Google C++ style guide instead discourages this potentially dangerous pattern entirely.
I'd also recommend against using mutable static variables since they're inherently not threadsafe, unless protected with atomics or a mutex. While a multiple
shared_ptr instances managing the same object can be used concurrently, concurrent access to the same
shared_ptr object/variable is only allowed with additional synchronization.