int x for this variable the compiler needs to know
- when to create memory for it
- when to destroy it aka how long does it live
- does another
.cppfile refer to this same one - are per thread copies
The default scope boundaries are commonly known, eccentrics are harder.
automatic storage duration
This is the default, scope bound duration. Memory is usually allocated on the stack frame for these.
static
static couples tightly with the declaration and behavior varies on where it is
declared.
- block scope
static storage duration aka lifetime becomes program execution.
void f() {
static int x = 0;
++x;
}- namespace scope
A namespace scoped variable already has static storage duration; so what will static do?
What is internal vs external linkage?
It answers whether the same “qualified name” = namespace + name appearing in different translation units or scopes refers to the same object.
static scross namespaces
This table summarises it cleanly
a.cpp | b.cpp | Linkage | Same entity? | Valid? |
|---|---|---|---|---|
static int x = 10; | static int x = 20; | internal / internal | No | Yes |
static int x = 10; | int x = 20; | internal / external | No | Yes |
int x = 10; | int x = 20; | external / external | Yes | No — two definitions |
static across blocks
| Namespace scope | Block scope | Namespace x linkage | Block x linkage | Same entity? | Valid? |
|---|---|---|---|---|---|
static int x = 10; | static int x = 20; | internal | none | No | Yes |
static int x = 10; | int x = 20; | internal | none | No | Yes |
int x = 10; | static int x = 20; | external | none | No | Yes |
int x = 10; | int x = 20; | external | none | No | Yes |
extern
extern can be treated simply as just a “don’t define storage for it, it’ll be made available
from someplace else”
Across translation units but same namespace
a.cpp | b.cpp | Same entity? | Valid? | Meaning |
|---|---|---|---|---|
int x = 10; | extern int x; | Yes | Yes | b.cpp refers to a.cpp’s x |
extern int x; | extern int x; | Intended same external entity | Only if defined somewhere | Neither line defines x |
static int x = 10; | extern int x; | No | Yes but b needs defined somewhere | a.cpp’s x is internal; b.cpp cannot reach it |
Across scopes
| Namespace scope | Block scope | Same entity? | Result |
|---|---|---|---|
int x = 10; | extern int x; | Yes | block extern redeclares the namespace x |
static int x = 10; | extern int x; | Yes | block extern redeclares the existing internal-linkage x |
extern int x; | static int x = 20; | No | block static x creates a new local object and hides the namespace x |
This last one is a bit special as in this case x is not given an external linkage.
But in general, this model that static gives internal linkage + full lifetime while external gives external linkage but needs it define from somewhere else will be a correct model in most cases
thread local
This’ll create one separate instance per thread. I’m skipping context where this is combined with static and extern ( yes,that’s possible which might seems trange because now we are combining modifiers )
mutable
This is not really a storage class specifier, all it says is that even if the parent object is const this can be modified.
struct A
{
mutable int x;
};
const A a;
a.x = 5; // allowedSome deleted ones
Historically we had an auto for automatic storage and a register to hint that
a value is to be kept in register as it’ll be heaviy used.