Reputation:
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 ?
Upvotes: 2
Views: 822
Reputation: 11230
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.
The short version is that constants can only be optimized out if they fit the following criteria:
There are several cases that will affect storage backing
inline
(C++17),inline
(C++17) and ODR used,Lets break down these different sections:
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.
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.
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{};
}
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);
}
inline
(C++17) with ODR useIf 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)
}
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.
Upvotes: 3
Reputation: 17464
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.
Upvotes: 2