1
In case A
there is a difference between global or namespace scope (internal linkage) and class scope (external linkage). So
// header.hpp
constexpr T A = <some value>; // internal linkage
namespace Nm { constexpr T A = <some value>; } // internal linkage
class Cl { public: static constexpr T A = <some value>; }; // not enough!
Consider the following usage:
// user.cpp
std::cout << A << Nm::A << Cl::A; // ok
std::cout << &A << &Nm::A; // ok
std::cout << &Cl::A; // linker error: undefined reference to `Cl::A'
Placing Cl::A
definition in source.cpp (in addition to the above Cl::A
declaration) eliminates this error:
// source.cpp
constexpr T Cl::A;
External linkage means that there would always be only one instance of Cl::A
. So Cl::A
seems to be a very good candidate for large T
. However: can we be sure that static initialization order fiasco would not present itself in this case? I believe that the answer is yes, because Cl::A
is constructed at compile-time.
I have tested A
, B
, a
alternatives with g++ 4.8.2 and 4.9.0, clang++ 3.4 on GNU/Linux platform. The results for three translation units:
A
in class scope with definition in source.cpp was both immune to fiasco and had the same address in all translation units even at compile-time.
A
in namespace or global scope had 3 different addresses both for large array and constexpr const char * A = "A";
(because of internal linkage).
B
(std::array<long double, 100>
) in any scope had 2 different addresses (address was the same in 2 translation units); additionally all 3 B
addresses suggested some different memory location (they were much bigger than other addresses) - I suspect that array was copied in memory at runtime.
a
when used with constexpr
types T
, e.g. int
, const char *
, std::array
, AND initialized with constexpr
expression in source.cpp, was as good as A
: immune to fiasco and had the same address in all translation units. If constant of constexpr
type T
is initialized with non-constexpr
, e.g. std::time(nullptr)
, and used before initialization, it would contain default value (for example, 0
for int
). It means that constant's value can depend on static initialization order in this case. So, do not initialize a
with non-constexpr
value!
The bottom line
- prefer
A
in class scope for any constexpr constant in most cases because it combines perfect safety, simplicity, memory saving and performance.
a
(initialized with constexpr
value in source.cpp!) should be used if namespace scope is preferable or it is desirable to avoid initialization in header.hpp (in order to reduce dependencies and compilation time). a
has one disadvantage compared to A
: it can be used in compile-time expressions only in source.cpp and only after initialization.
B
should be used for small T
in some cases: when namespace scope is preferable or template compile-time constant is needed (pi
for example). Also B
can be used when constant's value is rarely used or used only in exceptional situations, e.g. error messages.
- Other alternatives should almost never be used as they would rarely suit better than all 3 before-mentioned ways.
A
in namespace scope should not be used because it can potentially lead to N instances of constant, hence consume sizeof(T) * N
bytes of memory and cause cache misses. Here N equals to the number of translation units that include header.hpp. As noted in this proposal, A
in namespace scope can violate ODR if used in inline function.
C
could be used for big T
(B
is usually better for small T
) in 2 rare scenarios: when function call is preferable; when namespace scope AND initializing in header is preferable.
D
could be used when function call AND initializing in source file is preferable.
- The only shortcoming of
C
compared to A
and B
- its return value can not be used in compile-time expression. D
suffers from the same shortcoming and another one: function call run-time performance penalty (because it can not be inlined).
2
Avoid using non-constexpr
a
because of static initialization order fiasco. Consider a
only in case of sure bottleneck. Otherwise, safety is more important than small performance gain. b
, c
and d
are much safer. However c
and d
have 2 safety requirements:
for (auto f : {
all c
and d
-like functions}) {
T
constructor must not call f
because if the initialization of static local variable recursively enters the block in which the variable is being initialized, the behavior is undefined. This is not difficult to ensure.
- For each class
X
such that X::~X
calls f
and there is a statically initialized X
object: X::X
must call f
. The reason is that otherwise static const T
from f
could be constructed after and therefore destructed before global X
object; then X::~X
would cause UB. This requirement is much more difficult to guarantee than the previous one. So it almost prohibits global or static local variables with complicated destructors that use global constants. If destructor of statically initialized variable is not complicated, e.g. uses f()
for logging purposes, then placing f();
in the corresponding constructor ensures safety.
}
Note: these 2 requirements do not apply to C
and D
:
- the recursive call to
f
would not compile;
static constexpr T
constants in C
and D
are constructed at compile time - before any non-trivial variable is constructed, so they are destructed after all non-trivial variables' destruction (destructors are called in reverse order).
Note 2: C++ FAQ suggests a different implementation of c
and d
, which does not impose the second safety requirement. However in this case static constant is never destructed, which can interfere with memory leak detection, e.g. Valgrind diagnostic. Memory leaks, however benign, should be avoided. So these modified versions of c
and d
should be used only in exceptional situations.
One more alternative to consider here is a constant with internal linkage:
// header.hpp
namespace Ns { namespace { const T a1 = <some value>; } }
This approach has the same big downside as A
in namespace scope: internal linkage can create as many copies of a1
as the number of translation units that include header.hpp. It can also violate ODR in the same way as A
. However, since other options for non-constexpr
are not as good as for constexpr
constants, this alternative actually could have some rare use. BUT: this "solution" is still prone to static initialization order fiasco in case when a1
is used in public function which in turn is used for initialization of a global object. So introducing internal linkage does not solve the problem - just hides it, makes it less likely, probably more difficult to locate and fix.
The bottom line
c
provides the best performance and saves memory because it facilitates reusing exactly one T
instance and can be inlined, so it should be used in most cases.
d
is as good as c
for saving memory but is worse for performance as it would never be inlined. However d
can be used to reduce compilation time.
- consider
b
for small types or for rarely used constants (in rarely-used-constant case its definition can be moved to source.cpp to avoid recompilation on change). Also b
is the only solution if safety requirements for c
and d
can not be satisfied. b
is definitely not good for large T
if constant is used often, because the constant has to be constructed each time b
is called.
Note: there is another compile-time issue of inline functions and variables initialized in header.hpp. If constant's definition depends on another constant declared in a different header bad.h, and header bad.h should not be included in header.hpp, then D
, d
, a
and modified b
(with definition moved to source.cpp) are the only alternatives.