The storage class specifiers are
storage-class-specifier: static thread_local extern mutable
At most one storage-class-specifier shall appear in a given decl-specifier-seq, except that thread_local may appear with static or extern. If thread_local appears in any declaration of a variable it shall be present in all declarations of that entity. If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list or member-declarator-list of the declaration shall not be empty (except for an anonymous union declared in a named namespace or in the global namespace, which shall be declared static ([class.union.anon])). The storage-class-specifier applies to the name declared by each init-declarator in the list and not to any names declared by other specifiers. A storage-class-specifier other than thread_local shall not be specified in an explicit specialization or an explicit instantiation directive.
[ Note: A variable declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default. — end note ]
The thread_local specifier indicates that the named entity has thread storage duration. It shall be applied only to the names of variables of namespace or block scope and to the names of static data members. When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if no other storage-class-specifier appears in the decl-specifier-seq.
The static specifier can be applied only to names of variables and functions and to anonymous unions. There can be no static function declarations within a block, nor any static function parameters. A static specifier used in the declaration of a variable declares the variable to have static storage duration, unless accompanied by the thread_local specifier, which declares the variable to have thread storage duration. A static specifier can be used in declarations of class members; [class.static] describes its effect. For the linkage of a name declared with a static specifier, see [basic.link].
The extern specifier can be applied only to the names of variables and functions. The extern specifier cannot be used in the declaration of class members or function parameters. For the linkage of a name declared with an extern specifier, see [basic.link]. [ Note: The extern keyword can also be used in explicit-instantiations and linkage-specifications, but it is not a storage-class-specifier in such contexts. — end note ]
The linkages implied by successive declarations for a given entity shall agree. That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage. Each function in a given set of overloaded functions can have a different linkage, however. [ Example:
static char* f(); // f() has internal linkage char* f() // f() still has internal linkage { /* ... */ } char* g(); // g() has external linkage static char* g() // error: inconsistent linkage { /* ... */ } void h(); inline void h(); // external linkage inline void l(); void l(); // external linkage inline void m(); extern void m(); // external linkage static void n(); inline void n(); // internal linkage static int a; // a has internal linkage int a; // error: two definitions static int b; // b has internal linkage extern int b; // b still has internal linkage int c; // c has external linkage static int c; // error: inconsistent linkage extern int d; // d has external linkage static int d; // error: inconsistent linkage
— end example ]
The name of a declared but undefined class can be used in an extern declaration. Such a declaration can only be used in ways that do not require a complete class type. [ Example:
struct S; extern S a; extern S f(); extern void g(S); void h() { g(a); // error: S is incomplete f(); // error: S is incomplete }
— end example ]
The mutable specifier shall appear only in the declaration of a non-static data member whose type is neither const-qualified nor a reference type. [ Example:
class X { mutable const int* p; // OK mutable int* const q; // ill-formed };
— end example ]
The mutable specifier on a class data member nullifies a const specifier applied to the containing class object and permits modification of the mutable class member even though the rest of the object is const ([dcl.type.cv]).