If we have a static const
class member the address of which is never used, can the member be optimized away and allocated no storage ?

- 203,559
- 14
- 181
- 302
-
2Judging by what g++ [output](https://godbolt.org/z/djazEq), I'm going to assume yes. – ChrisMM Jul 09 '20 at 16:22
-
3If removing something doesn't alter the behaviour of the program the answer is usually "yes". – tadman Jul 09 '20 at 16:25
-
1A constant that is never used can be *optimized away*. In another view, since it is not used, it won't show up in the code. – Thomas Matthews Jul 09 '20 at 16:30
-
@ChrisMM The question is asking about `static const` **class member**s -- the example you demonstrate is for constants with `static` declaring internal linkage -- which is different for the optimizer. – Human-Compiler Jul 09 '20 at 16:31
-
@ThomasMatthews: "not odr-used" != "not used". – Jarod42 Jul 09 '20 at 16:31
-
@Human-Compiler, you're right, I missed the 'class' in the question. Seems then it depends on [how they're defined](https://godbolt.org/z/zns8hT) (for g++ at least) – ChrisMM Jul 09 '20 at 16:36
-
1@YvesDaoust: With as-if rule, I would say yes. – Jarod42 Jul 09 '20 at 16:37
-
1@YvesDaoust: There are two answers to this question. If either of them answer it to your satisfaction, please remember to select an answer as correct, or leave feedback as to how the answers may be improved. – Human-Compiler Jul 17 '20 at 03:18
2 Answers
The answer is it depends on context.
The compiler must follow the as-if rule, which ensures that the program must behave the same way optimized as it would unoptimized -- so it can only remove constants that it knows will not affect the behavior of syntactically valid code.
Short Version
The short version is that constants can only be optimized out if they fit the following criteria:
- Constants are trivial, or have visible constructors / destructors that do not affect external state,
- This effect is transitive. If a constructor / destructor calls a non-visible function, the compiler must assume that the call may alter external state, and is thus significant.
- Constants are not ODR used, and
- Constants are either anonymously defined (in an unnamed namespace), or inline defined
Long Version
There are several cases that will affect storage backing
- Whether the constant has been given explicit storage backing,
- Whether the constant exists in an unnamed namespace,
- Whether the constant has non-trivial, non visible constructors/destructors,
- Whether the constant is defined
inline
(C++17), - Whether the constant is defined
inline
(C++17) and ODR used, - Whether the constant is defined in-line, but not given storage-backing
Lets break down these different sections:
1. Constant is given explicit storage backing
If you define storage backing from the start, e.g.:
struct example {
static const int value;
};
const int example::value = 5;
Then there will usually be storage backing, since the compiler has to assume it will be ODR-used eventually -- even if the constant is private.
2. Constant is in an unnamed namespace
However, if these types are defined in an unnamed namespace, which makes them anonymous symbols for the translation unit -- then lack-of-use may result in it being optimized away:
namespace {
struct example {
static const int value;
};
const int example::value = 5;
}
This will only work for trivial constructors / destructors, or constructors / destructors that the compiler can currently see in order to optimize out instructions.
3. Constant is in an unnamed namespace with non-trivial constructor / destructor
If the constant above has a non-trivial constructor or destructor, or a constructor/destructor that is not visible, the constants will have to be emitted:
Either:
namespace {
struct actor{
actor(); // not defined
~actor(); // not defined
};
class example {
static const actor value;
};
const actor example::value{};
}
or:
extern int some_global;
namespace {
struct actor{
actor(){ ::some_global = 5;}
~actor(){ ::some_global = 10;}
};
class example {
static const actor value;
};
const actor example::value{};
}
4. Constant is defined inline
(C++17)
If you define an inline static const
or inline static constexpr
value in C++17, then it depends on whether it is ODR-used for whether the symbol will ever be emitted.
No ODR-use -- No emission:
struct example {
inline static const int value = 5;
};
void test()
{
std::printf("%d", example::value);
}
5. Constant is defined inline
(C++17) with ODR use
If the symbol is inline
but is ODR used, the storage backing must be emitted.
ODR use -- Emission:
struct example {
inline static const int value = 5;
};
void consume(const int&);
void test()
{
consume(example::value)
}
6. Constant is defined in-line, but not given storage-backing
In the case that you have an inline definition of a static const
object (without C++17's inline
), there should never be storage backing. The constants can only be created by constant expressions (with or without constexpr
), but have no explicitly stated storage-backing -- meaning that they cannot be ODR-used unless declared inline
.
This is what is leveraged by C++'s type-traits, since they do not produce any additional code since they amount to compile-time constant objects.
struct example {
static const int value = 5;
};
Edit: One more note that I wanted to add: If there is any storage-backing specified to a symbol with non-internal linkage, either through explicit definitions or via ODR use with an inline
symbol, the compiler/linker should not be able to optimize this backing out. At least, not from a proper interpretation of the as-if rule (though certain non-standard optimization flags may permit this).
The issue is that the compiler must assume that symbols with external linkage may still be named or referenced elsewhere, even if the symbol is private
and never accessible via friend
-ship. C++ has sharp corners where you can reference private members in template parameters via explicit template specializations. So even though it should logically not be accessible, it actually is from the language (and thus, compiler's) view.

- 11,022
- 1
- 32
- 59
Yes, but only link-time optimisation is capable of doing this, because only the link stage can prove that the address is never taken throughout the whole program. And, if you're producing a shared library, that's just off the table. Speaking generally, you're best off assuming that the object probably will take up space in your executable.
Regardless of whether storage is provided, though, you can certainly expect the compiler to "inline" use of the value, if the initialiser is visible in the same translation unit. You can observe this by failing to provide a separate definition, then doing anything that doesn't involve ODR-use and noticing that you may not get an undefined reference link error.

- 17,071
- 2
- 21
- 35