There are plenty of questions and answers relating to C++ global variables, such as:
- static and extern global variables in C and C++
- Global variables and constexpr (inline or not?)
- Is there any sense to declare static global variable as inline?
- Are global variables extern by default or is it equivalent to declaring variable with extern in global?
- Defining global constants in C++1z?
- Defining global constant in C++
- inline static constexpr vs global inline constexpr
They all contain small fragments of information, such as how inline variables work in C++17 specifically, etc. Some of them also haven't aged well, since C++17 fundamentally changed the rules by introducing inline variables.
C++20 also introduces modules and module linkage, yet again making most content on StackOverflow out-of-date, or even obsolete.
This Q&A is an attempt at unifying these questions, and at giving readers an overview that considers these version changes.
Questions
- When do I use
static,inline,extern,const,constexpretc. for global variables? - How does the answer change historically (before/after C++17, before/after C++20)?
- How does the answer change depending on where the global variable is located (header/source/module)?
0. Overview
(i.e. declared and only used in a
single file, not declared in a header)
static const,static constexpr(C++11), orconstin anonymous namespace(C++11)static, orin anonymous namespace(C++11)
defined in a source file
extern constin header;constin source, orconstexpr(C++11) in sourceexternin header;plain in source
until C++17
inlinewith templatesand
constorconstexpr(C++11); orenumfor integers onlyinlinewith templatessince C++17
inline const, orinline constexprinlineconstorconstexpr;optionally
inlineinlineexport constorexport inline constexprexport;optionally
inlineIn all cases above,
constinit(since C++20) may also be used, but not in combination withconstexpr.constinit consthas it uses too, and is not the same asconstexpr.Note: the decision may also change based on whether the global variables are defined/used in dynamically linked libraries, and other factors.
1. Always use ensure that everything local to one source file has internal linkage
First of all, if the global variable is declared in, and only used in a single source file, then it must have internal linkage. A global variable has internal linkage when:
staticconst(orconstexpr(since C++11), sinceconstexprimpliesconst)If it doesn't have internal linkage, then you could easily run into an ODR violation. The example below is ill-formed, no diagnostic required.
Making the variable
inline(since C++17) does not solve this issue. Internal linkage makes it safe, because the twocounters would be distinct in each TU.2. If something is declared, but not defined in a header, make it
externSometimes, it's not important for everyone to have a definition. For example:
It would be pointless to put the definition of
log_fileto be in a header, and thus visible everywhere. Very little can be gained in terms of performance, and it would force us to makeopen_log_file()visible everywhere too.Note on
extern constexpr(since C++11) orextern const constinit(since C++20)Another valid but rare use case is
extern constexpr(since C++11) (see this answer), i.e.extern constin a header,constexpr(since C++11) in a source. If available, this is better expressed throughconst constinit(since C++20) in the source.The purpose of this pattern is to avoid dynamic initialization for expensive-to-initialize look-up tables while keeping a header/source split.
3. If something is defined in a header, make it
inline(since C++17), or imitateinlinewith templates(until C++17)Sometimes, it is important to have a definition everywhere, such as for global constants that should be inlined:
Q: Do I really need
inlinein combination withconstexpr?Yes, you do. Consider the following example:
This program may be ill-formed, no diagnostic required, because
exponenthas internal linkage (due to beingconst) and is a distinct object in every TU. Each definition offoomay return a different reference to its own uniqueexponent, which is a violation of [basic.def.odr] p14.5Q: What can I do prior to C++17?
inlinevariables don't exist yet.A similar mechanism has always existed in the form of templates.
Alternatively, specifically for scoped(since C++11) and unscoped enumerations, you can put the definition in a header without risk:
4. Make all global variables
constor evenconstexpr(since C++11) whenever possibleIn addition to the rules in 1., 2., 3., always make things
constwhen they can be. This hugely simplifies ensuring correctness of your program, and enables additional compiler optimizations.Q: What do I do if initialization is complicated?
Sometimes you can't just initialize a global with a simple expression, like here:
However, don't late-initialize; instead use an immediately invoked lambda expression (IILE)(since C++11) or a regular function to perform initialization.
Q: What impact does
constorconstexpr(since C++11) have on linkage?Making a global variable
constorconstexpr(since C++11) gives it internal linkage, but as explained in 3., this doesn't simplify anything for you. You still have to worry about linkage and the ODR.5. Static data members work differently
As seen in 3., static data members might not follow the same rules. They have the same linkage as the class they belong to, with the following consequences:
inlinein the case of class templates.constdoes not imply internal linkage for static data members.Also,
constexprfor static data members impliesinline(since C++17).