[c++] What does 'const static' mean in C and C++?

const static int foo = 42;

I saw this in some code here on StackOverflow and I couldn't figure out what it does. Then I saw some confused answers on other forums. My best guess is that it's used in C to hide the constant foo from other modules. Is this correct? If so, why would anyone use it in a C++ context where you can just make it private?

This question is related to c++ c

The answer is


Yes, it hides a variable in a module from other modules. In C++, I use it when I don't want/need to change a .h file that will trigger an unnecessary rebuild of other files. Also, I put the static first:

static const int foo = 42;

Also, depending on its use, the compiler won't even allocate storage for it and simply "inline" the value where it's used. Without the static, the compiler can't assume it's not being used elsewhere and can't inline.


This ia s global constant visible/accessible only in the compilation module (.cpp file). BTW using static for this purpose is deprecated. Better use an anonymous namespace and an enum:

namespace
{
  enum
  {
     foo = 42
  };
}

In C++,

static const int foo = 42;

is the preferred way to define & use constants. I.e. use this rather than

#define foo 42

because it doesn't subvert the type-safety system.


Making it private would still mean it appears in the header. I tend to use "the weakest" way that works. See this classic article by Scott Meyers: http://www.ddj.com/cpp/184401197 (it's about functions, but can be applied here as well).


This ia s global constant visible/accessible only in the compilation module (.cpp file). BTW using static for this purpose is deprecated. Better use an anonymous namespace and an enum:

namespace
{
  enum
  {
     foo = 42
  };
}

To all the great answers, I want to add a small detail:

If You write plugins (e.g. DLLs or .so libraries to be loaded by a CAD system), then static is a life saver that avoids name collisions like this one:

  1. The CAD system loads a plugin A, which has a "const int foo = 42;" in it.
  2. The system loads a plugin B, which has "const int foo = 23;" in it.
  3. As a result, plugin B will use the value 42 for foo, because the plugin loader will realize, that there is already a "foo" with external linkage.

Even worse: Step 3 may behave differently depending on compiler optimization, plugin load mechanism, etc.

I had this issue once with two helper functions (same name, different behaviour) in two plugins. Declaring them static solved the problem.


That line of code can actually appear in several different contexts and alghough it behaves approximately the same, there are small differences.

Namespace Scope

// foo.h
static const int i = 0;

'i' will be visible in every translation unit that includes the header. However, unless you actually use the address of the object (for example. '&i'), I'm pretty sure that the compiler will treat 'i' simply as a type safe 0. Where two more more translation units take the '&i' then the address will be different for each translation unit.

// foo.cc
static const int i = 0;

'i' has internal linkage, and so cannot be referred to from outside of this translation unit. However, again unless you use its address it will most likely be treated as a type-safe 0.

One thing worth pointing out, is that the following declaration:

const int i1 = 0;

is exactly the same as static const int i = 0. A variable in a namespace declared with const and not explicitly declared with extern is implicitly static. If you think about this, it was the intention of the C++ committee to allow const variables to be declared in header files without always needing the static keyword to avoid breaking the ODR.

Class Scope

class A {
public:
  static const int i = 0;
};

In the above example, the standard explicitly specifies that 'i' does not need to be defined if its address is not required. In other words if you only use 'i' as a type-safe 0 then the compiler will not define it. One difference between the class and namespace versions is that the address of 'i' (if used in two ore more translation units) will be the same for the class member. Where the address is used, you must have a definition for it:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address

It's a small space optimization.

When you say

const int foo = 42;

You're not defining a constant, but creating a read-only variable. The compiler is smart enough to use 42 whenever it sees foo, but it will also allocate space in the initialized data area for it. This is done because, as defined, foo has external linkage. Another compilation unit can say:

extern const int foo;

To get access to its value. That's not a good practice since that compilation unit has no idea what the value of foo is. It just knows it's a const int and has to reload the value from memory whenever it is used.

Now, by declaring that it is static:

static const int foo = 42;

The compiler can do its usual optimization, but it can also say "hey, nobody outside this compilation unit can see foo and I know it's always 42 so there is no need to allocate any space for it."

I should also note that in C++, the preferred way to prevent names from escaping the current compilation unit is to use an anonymous namespace:

namespace {
    const int foo = 42; // same as static definition above
}

This ia s global constant visible/accessible only in the compilation module (.cpp file). BTW using static for this purpose is deprecated. Better use an anonymous namespace and an enum:

namespace
{
  enum
  {
     foo = 42
  };
}

That line of code can actually appear in several different contexts and alghough it behaves approximately the same, there are small differences.

Namespace Scope

// foo.h
static const int i = 0;

'i' will be visible in every translation unit that includes the header. However, unless you actually use the address of the object (for example. '&i'), I'm pretty sure that the compiler will treat 'i' simply as a type safe 0. Where two more more translation units take the '&i' then the address will be different for each translation unit.

// foo.cc
static const int i = 0;

'i' has internal linkage, and so cannot be referred to from outside of this translation unit. However, again unless you use its address it will most likely be treated as a type-safe 0.

One thing worth pointing out, is that the following declaration:

const int i1 = 0;

is exactly the same as static const int i = 0. A variable in a namespace declared with const and not explicitly declared with extern is implicitly static. If you think about this, it was the intention of the C++ committee to allow const variables to be declared in header files without always needing the static keyword to avoid breaking the ODR.

Class Scope

class A {
public:
  static const int i = 0;
};

In the above example, the standard explicitly specifies that 'i' does not need to be defined if its address is not required. In other words if you only use 'i' as a type-safe 0 then the compiler will not define it. One difference between the class and namespace versions is that the address of 'i' (if used in two ore more translation units) will be the same for the class member. Where the address is used, you must have a definition for it:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address

According to C99/GNU99 specification:

  • static

    • is storage-class specifier

    • objects of file level scope by default has external linkage

    • objects of file level scope with static specifier has internal linkage
  • const

    • is type-qualifier (is a part of type)

    • keyword applied to immediate left instance - i.e.

      • MyObj const * myVar; - unqualified pointer to const qualified object type

      • MyObj * const myVar; - const qualified pointer to unqualified object type

    • Leftmost usage - applied to the object type, not variable

      • const MyObj * myVar; - unqualified pointer to const qualified object type

THUS:

static NSString * const myVar; - constant pointer to immutable string with internal linkage.

Absence of the static keyword will make variable name global and might lead to name conflicts within the application.


It's missing an 'int'. It should be:

const static int foo = 42;

In C and C++, it declares an integer constant with local file scope of value 42.

Why 42? If you don't already know (and it's hard to believe you don't), it's a refernce to the Answer to Life, the Universe, and Everything.


Yes, it hides a variable in a module from other modules. In C++, I use it when I don't want/need to change a .h file that will trigger an unnecessary rebuild of other files. Also, I put the static first:

static const int foo = 42;

Also, depending on its use, the compiler won't even allocate storage for it and simply "inline" the value where it's used. Without the static, the compiler can't assume it's not being used elsewhere and can't inline.


It's a small space optimization.

When you say

const int foo = 42;

You're not defining a constant, but creating a read-only variable. The compiler is smart enough to use 42 whenever it sees foo, but it will also allocate space in the initialized data area for it. This is done because, as defined, foo has external linkage. Another compilation unit can say:

extern const int foo;

To get access to its value. That's not a good practice since that compilation unit has no idea what the value of foo is. It just knows it's a const int and has to reload the value from memory whenever it is used.

Now, by declaring that it is static:

static const int foo = 42;

The compiler can do its usual optimization, but it can also say "hey, nobody outside this compilation unit can see foo and I know it's always 42 so there is no need to allocate any space for it."

I should also note that in C++, the preferred way to prevent names from escaping the current compilation unit is to use an anonymous namespace:

namespace {
    const int foo = 42; // same as static definition above
}

It's missing an 'int'. It should be:

const static int foo = 42;

In C and C++, it declares an integer constant with local file scope of value 42.

Why 42? If you don't already know (and it's hard to believe you don't), it's a refernce to the Answer to Life, the Universe, and Everything.


C++17 inline variables

If you Googled "C++ const static", then this is very likely what you really want to use are C++17 inline variables.

This awesome C++17 feature allow us to:

  • conveniently use just a single memory address for each constant
  • store it as a constexpr: How to declare constexpr extern?
  • do it in a single line from one header

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Compile and run:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub upstream.

See also: How do inline variables work?

C++ standard on inline variables

The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":

6 An inline function or variable with external linkage shall have the same address in all translation units.

cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static is not given, then it has external linkage.

GCC inline variable implementation

We can observe how it is implemented with:

nm main.o notmain.o

which contains:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

and man nm says about u:

"u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

so we see that there is a dedicated ELF extension for this.

Pre-C++ 17: extern const

Before C++ 17, and in C, we can achieve a very similar effect with an extern const, which will lead to a single memory location being used.

The downsides over inline are:

  • it is not possible to make the variable constexpr with this technique, only inline allows that: How to declare constexpr extern?
  • it is less elegant as you have to declare and define the variable separately in the header and cpp file

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHub upstream.

Pre-C++17 header only alternatives

These are not as good as the extern solution, but they work and only take up a single memory location:

A constexpr function, because constexpr implies inline and inline allows (forces) the definition to appear on every translation unit:

constexpr int shared_inline_constexpr() { return 42; }

and I bet that any decent compiler will inline the call.

You can also use a const or constexpr static variable as in:

#include <iostream>

struct MyClass {
    static constexpr int i = 42;
};

int main() {
    std::cout << MyClass::i << std::endl;
    // undefined reference to `MyClass::i'
    //std::cout << &MyClass::i << std::endl;
}

but you can't do things like taking its address, or else it becomes odr-used, see also: Defining constexpr static data members

C

In C the situation is the same as C++ pre C++ 17, I've uploaded an example at: What does "static" mean in C?

The only difference is that in C++, const implies static for globals, but it does not in C: C++ semantics of `static const` vs `const`

Any way to fully inline it?

TODO: is there any way to fully inline the variable, without using any memory at all?

Much like what the preprocessor does.

This would require somehow:

  • forbidding or detecting if the address of the variable is taken
  • add that information to the ELF object files, and let LTO optimize it up

Related:

Tested in Ubuntu 18.10, GCC 8.2.0.


A lot of people gave the basic answer but nobody pointed out that in C++ const defaults to static at namespace level (and some gave wrong information). See the C++98 standard section 3.5.3.

First some background:

Translation unit: A source file after the pre-processor (recursively) included all its include files.

Static linkage: A symbol is only available within its translation unit.

External linkage: A symbol is available from other translation units.

At namespace level

This includes the global namespace aka global variables.

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

At function level

static means the value is maintained between function calls.
The semantics of function static variables is similar to global variables in that they reside in the program's data-segment (and not the stack or the heap), see this question for more details about static variables' lifetime.

At class level

static means the value is shared between all instances of the class and const means it doesn't change.


To all the great answers, I want to add a small detail:

If You write plugins (e.g. DLLs or .so libraries to be loaded by a CAD system), then static is a life saver that avoids name collisions like this one:

  1. The CAD system loads a plugin A, which has a "const int foo = 42;" in it.
  2. The system loads a plugin B, which has "const int foo = 23;" in it.
  3. As a result, plugin B will use the value 42 for foo, because the plugin loader will realize, that there is already a "foo" with external linkage.

Even worse: Step 3 may behave differently depending on compiler optimization, plugin load mechanism, etc.

I had this issue once with two helper functions (same name, different behaviour) in two plugins. Declaring them static solved the problem.


In C++,

static const int foo = 42;

is the preferred way to define & use constants. I.e. use this rather than

#define foo 42

because it doesn't subvert the type-safety system.


A lot of people gave the basic answer but nobody pointed out that in C++ const defaults to static at namespace level (and some gave wrong information). See the C++98 standard section 3.5.3.

First some background:

Translation unit: A source file after the pre-processor (recursively) included all its include files.

Static linkage: A symbol is only available within its translation unit.

External linkage: A symbol is available from other translation units.

At namespace level

This includes the global namespace aka global variables.

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

At function level

static means the value is maintained between function calls.
The semantics of function static variables is similar to global variables in that they reside in the program's data-segment (and not the stack or the heap), see this question for more details about static variables' lifetime.

At class level

static means the value is shared between all instances of the class and const means it doesn't change.


It's missing an 'int'. It should be:

const static int foo = 42;

In C and C++, it declares an integer constant with local file scope of value 42.

Why 42? If you don't already know (and it's hard to believe you don't), it's a refernce to the Answer to Life, the Universe, and Everything.


Making it private would still mean it appears in the header. I tend to use "the weakest" way that works. See this classic article by Scott Meyers: http://www.ddj.com/cpp/184401197 (it's about functions, but can be applied here as well).


According to C99/GNU99 specification:

  • static

    • is storage-class specifier

    • objects of file level scope by default has external linkage

    • objects of file level scope with static specifier has internal linkage
  • const

    • is type-qualifier (is a part of type)

    • keyword applied to immediate left instance - i.e.

      • MyObj const * myVar; - unqualified pointer to const qualified object type

      • MyObj * const myVar; - const qualified pointer to unqualified object type

    • Leftmost usage - applied to the object type, not variable

      • const MyObj * myVar; - unqualified pointer to const qualified object type

THUS:

static NSString * const myVar; - constant pointer to immutable string with internal linkage.

Absence of the static keyword will make variable name global and might lead to name conflicts within the application.


A lot of people gave the basic answer but nobody pointed out that in C++ const defaults to static at namespace level (and some gave wrong information). See the C++98 standard section 3.5.3.

First some background:

Translation unit: A source file after the pre-processor (recursively) included all its include files.

Static linkage: A symbol is only available within its translation unit.

External linkage: A symbol is available from other translation units.

At namespace level

This includes the global namespace aka global variables.

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

At function level

static means the value is maintained between function calls.
The semantics of function static variables is similar to global variables in that they reside in the program's data-segment (and not the stack or the heap), see this question for more details about static variables' lifetime.

At class level

static means the value is shared between all instances of the class and const means it doesn't change.


To all the great answers, I want to add a small detail:

If You write plugins (e.g. DLLs or .so libraries to be loaded by a CAD system), then static is a life saver that avoids name collisions like this one:

  1. The CAD system loads a plugin A, which has a "const int foo = 42;" in it.
  2. The system loads a plugin B, which has "const int foo = 23;" in it.
  3. As a result, plugin B will use the value 42 for foo, because the plugin loader will realize, that there is already a "foo" with external linkage.

Even worse: Step 3 may behave differently depending on compiler optimization, plugin load mechanism, etc.

I had this issue once with two helper functions (same name, different behaviour) in two plugins. Declaring them static solved the problem.


It's missing an 'int'. It should be:

const static int foo = 42;

In C and C++, it declares an integer constant with local file scope of value 42.

Why 42? If you don't already know (and it's hard to believe you don't), it's a refernce to the Answer to Life, the Universe, and Everything.


Making it private would still mean it appears in the header. I tend to use "the weakest" way that works. See this classic article by Scott Meyers: http://www.ddj.com/cpp/184401197 (it's about functions, but can be applied here as well).


It's a small space optimization.

When you say

const int foo = 42;

You're not defining a constant, but creating a read-only variable. The compiler is smart enough to use 42 whenever it sees foo, but it will also allocate space in the initialized data area for it. This is done because, as defined, foo has external linkage. Another compilation unit can say:

extern const int foo;

To get access to its value. That's not a good practice since that compilation unit has no idea what the value of foo is. It just knows it's a const int and has to reload the value from memory whenever it is used.

Now, by declaring that it is static:

static const int foo = 42;

The compiler can do its usual optimization, but it can also say "hey, nobody outside this compilation unit can see foo and I know it's always 42 so there is no need to allocate any space for it."

I should also note that in C++, the preferred way to prevent names from escaping the current compilation unit is to use an anonymous namespace:

namespace {
    const int foo = 42; // same as static definition above
}

Yes, it hides a variable in a module from other modules. In C++, I use it when I don't want/need to change a .h file that will trigger an unnecessary rebuild of other files. Also, I put the static first:

static const int foo = 42;

Also, depending on its use, the compiler won't even allocate storage for it and simply "inline" the value where it's used. Without the static, the compiler can't assume it's not being used elsewhere and can't inline.


That line of code can actually appear in several different contexts and alghough it behaves approximately the same, there are small differences.

Namespace Scope

// foo.h
static const int i = 0;

'i' will be visible in every translation unit that includes the header. However, unless you actually use the address of the object (for example. '&i'), I'm pretty sure that the compiler will treat 'i' simply as a type safe 0. Where two more more translation units take the '&i' then the address will be different for each translation unit.

// foo.cc
static const int i = 0;

'i' has internal linkage, and so cannot be referred to from outside of this translation unit. However, again unless you use its address it will most likely be treated as a type-safe 0.

One thing worth pointing out, is that the following declaration:

const int i1 = 0;

is exactly the same as static const int i = 0. A variable in a namespace declared with const and not explicitly declared with extern is implicitly static. If you think about this, it was the intention of the C++ committee to allow const variables to be declared in header files without always needing the static keyword to avoid breaking the ODR.

Class Scope

class A {
public:
  static const int i = 0;
};

In the above example, the standard explicitly specifies that 'i' does not need to be defined if its address is not required. In other words if you only use 'i' as a type-safe 0 then the compiler will not define it. One difference between the class and namespace versions is that the address of 'i' (if used in two ore more translation units) will be the same for the class member. Where the address is used, you must have a definition for it:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address

A lot of people gave the basic answer but nobody pointed out that in C++ const defaults to static at namespace level (and some gave wrong information). See the C++98 standard section 3.5.3.

First some background:

Translation unit: A source file after the pre-processor (recursively) included all its include files.

Static linkage: A symbol is only available within its translation unit.

External linkage: A symbol is available from other translation units.

At namespace level

This includes the global namespace aka global variables.

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

At function level

static means the value is maintained between function calls.
The semantics of function static variables is similar to global variables in that they reside in the program's data-segment (and not the stack or the heap), see this question for more details about static variables' lifetime.

At class level

static means the value is shared between all instances of the class and const means it doesn't change.


Yes, it hides a variable in a module from other modules. In C++, I use it when I don't want/need to change a .h file that will trigger an unnecessary rebuild of other files. Also, I put the static first:

static const int foo = 42;

Also, depending on its use, the compiler won't even allocate storage for it and simply "inline" the value where it's used. Without the static, the compiler can't assume it's not being used elsewhere and can't inline.


Making it private would still mean it appears in the header. I tend to use "the weakest" way that works. See this classic article by Scott Meyers: http://www.ddj.com/cpp/184401197 (it's about functions, but can be applied here as well).


That line of code can actually appear in several different contexts and alghough it behaves approximately the same, there are small differences.

Namespace Scope

// foo.h
static const int i = 0;

'i' will be visible in every translation unit that includes the header. However, unless you actually use the address of the object (for example. '&i'), I'm pretty sure that the compiler will treat 'i' simply as a type safe 0. Where two more more translation units take the '&i' then the address will be different for each translation unit.

// foo.cc
static const int i = 0;

'i' has internal linkage, and so cannot be referred to from outside of this translation unit. However, again unless you use its address it will most likely be treated as a type-safe 0.

One thing worth pointing out, is that the following declaration:

const int i1 = 0;

is exactly the same as static const int i = 0. A variable in a namespace declared with const and not explicitly declared with extern is implicitly static. If you think about this, it was the intention of the C++ committee to allow const variables to be declared in header files without always needing the static keyword to avoid breaking the ODR.

Class Scope

class A {
public:
  static const int i = 0;
};

In the above example, the standard explicitly specifies that 'i' does not need to be defined if its address is not required. In other words if you only use 'i' as a type-safe 0 then the compiler will not define it. One difference between the class and namespace versions is that the address of 'i' (if used in two ore more translation units) will be the same for the class member. Where the address is used, you must have a definition for it:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address

To all the great answers, I want to add a small detail:

If You write plugins (e.g. DLLs or .so libraries to be loaded by a CAD system), then static is a life saver that avoids name collisions like this one:

  1. The CAD system loads a plugin A, which has a "const int foo = 42;" in it.
  2. The system loads a plugin B, which has "const int foo = 23;" in it.
  3. As a result, plugin B will use the value 42 for foo, because the plugin loader will realize, that there is already a "foo" with external linkage.

Even worse: Step 3 may behave differently depending on compiler optimization, plugin load mechanism, etc.

I had this issue once with two helper functions (same name, different behaviour) in two plugins. Declaring them static solved the problem.


C++17 inline variables

If you Googled "C++ const static", then this is very likely what you really want to use are C++17 inline variables.

This awesome C++17 feature allow us to:

  • conveniently use just a single memory address for each constant
  • store it as a constexpr: How to declare constexpr extern?
  • do it in a single line from one header

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Compile and run:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub upstream.

See also: How do inline variables work?

C++ standard on inline variables

The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":

6 An inline function or variable with external linkage shall have the same address in all translation units.

cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static is not given, then it has external linkage.

GCC inline variable implementation

We can observe how it is implemented with:

nm main.o notmain.o

which contains:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

and man nm says about u:

"u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

so we see that there is a dedicated ELF extension for this.

Pre-C++ 17: extern const

Before C++ 17, and in C, we can achieve a very similar effect with an extern const, which will lead to a single memory location being used.

The downsides over inline are:

  • it is not possible to make the variable constexpr with this technique, only inline allows that: How to declare constexpr extern?
  • it is less elegant as you have to declare and define the variable separately in the header and cpp file

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHub upstream.

Pre-C++17 header only alternatives

These are not as good as the extern solution, but they work and only take up a single memory location:

A constexpr function, because constexpr implies inline and inline allows (forces) the definition to appear on every translation unit:

constexpr int shared_inline_constexpr() { return 42; }

and I bet that any decent compiler will inline the call.

You can also use a const or constexpr static variable as in:

#include <iostream>

struct MyClass {
    static constexpr int i = 42;
};

int main() {
    std::cout << MyClass::i << std::endl;
    // undefined reference to `MyClass::i'
    //std::cout << &MyClass::i << std::endl;
}

but you can't do things like taking its address, or else it becomes odr-used, see also: Defining constexpr static data members

C

In C the situation is the same as C++ pre C++ 17, I've uploaded an example at: What does "static" mean in C?

The only difference is that in C++, const implies static for globals, but it does not in C: C++ semantics of `static const` vs `const`

Any way to fully inline it?

TODO: is there any way to fully inline the variable, without using any memory at all?

Much like what the preprocessor does.

This would require somehow:

  • forbidding or detecting if the address of the variable is taken
  • add that information to the ELF object files, and let LTO optimize it up

Related:

Tested in Ubuntu 18.10, GCC 8.2.0.


In C++,

static const int foo = 42;

is the preferred way to define & use constants. I.e. use this rather than

#define foo 42

because it doesn't subvert the type-safety system.