From en.cppreference.com/w/cpp/language/initialization:
Unordered dynamic initialization, which [sic] applies only to (static/thread-local) class template static data members and variable templates (since C++14) that aren't explicitly specialized.
Therefore static templates appear to be vulnerable to an even worse version of The Static Initialization Order Fiasco (TSIOF) (i.e. unordered within a translation unit).
Does use of constexpr remove this vulnerability?
i.e. is the output of the below code guaranteed to be success
?
Obviously, due to the nature of this question, working examples won't suffice as answers; quotes from the standard would be required. (C++17 answers preferred)
#include<cassert>
template<class T> static constexpr T a = 41;
template<class T> static constexpr T b = a<T>+1;
int main(){
assert(b<int> == 42);
std::cout <<"success\n";
}
BTW, if someone is an expert on this I have a related, unanswered question (that would be easy for such an expert to answer) here. Further, what would be the implications here if the answer to my other question is negative (i.e. constexpr doesn't help across translation units)?
UPDATE: I need to clarify what my concern is here. The original question title asked whether initialization order is a concern for constexpr template variables. I have clarified it. I am not concerned about whether dynamic initialization is taking place in the example; it isn't. My concern is that since ordered initialization cannot be assumed in the dynamic initialization case, can it be assumed in the constant initialization case? Prior to seeing the behavior of dynamically initialized template variables (within the same translation unit) I would have never even thought of this. However, since dynamically-initialized, static-duration template variables do not provide ordered initialization I now see no reason to assume that constant-initiliazed, static-duration template variables have guaranteed ordered initialization either. I need to be 100% sure that constant initialization of template variables takes place in the order of their definition within a TU.
Again, I see no reason to assume the constant-initializer-within-the-compiler is required to intialize in order if the dynamic initializer isn't. Absence of a warning in the standard that constant initialization is unordered does not suffice.
I realize that some may think this is excessive concern but I am working on safety-critical software and my company has placed adoption of C++14 on hold until this issue is resolved.
[Note that throughout this post,
a<T>
andb<T>
are shortened toa
andb
, respectively.][expr.const/3]
states that:Thus,
a
will be usable duringb
's initialisation, due to theconstexpr
specifier.constexpr
is fully applicable both for variables and variable templates, as stated in the first sentence of[dcl.constexpr/1]
.Thus, a variable or variable template declared
constexpr
is usable in constant expressions (including the initialisation of otherconstexpr
variables) after its own initialisation, with the value specified in its initialisation (due to constant initialisation being implictly guaranteed byconstexpr
).If we wish to explore the second option in
[expr.const/3]
rather than relying on the "constexpr
means yes" clause, that one will also lead toa
being usable duringb
's initialisation, albeit by a more circuitous route that involves delving into multiple parts of the standard and piecing together the implicit guarantees.First, would come the question of whether
a
has a constant initializer. To determine whether it has a constant initializer, we can consult[expr.const/2]
, which reads (note omitted):We have two explicit guarantees that the initialiser's full-expression is a constant expression, both from
[dcl.constexpr/9]
:a
is implicitlyconst
, and the full-expression not being a constant expression would be an error.This, by extension, also means that (as of C++14),
a
will not be subject zero initialisation ([basic.start.static/2]
, irrelevant parts omitted):To verify that
a
has the correct value after initialisation, if we want to be really thorough, we could then look at[intro.execution/9]
:As we know that
a
's initialisation is a full-expression, we are implicitly guaranteed thata
will have been assigned the value41
by the end of its full-expression, before the next full-expression (including other initialisations) in sequence is evaluated. By combining this with[expr.const/3]
and[basic.start.static/2]
, we are thus guaranteed that (as with theconstexpr
clause),a
is usable in constant expressions encountered after its own initialisation, such asb
's initialisation, and also guaranteed thata == 41 && a != 0
.Based on basic.start.static:
In your code:
is doing constant initialization which makes:
be initialized with
42
due to the templates' constant evaluation.which states that (From expr.const/8.7):
Thus, it is guaranteed the output is always
"success"
ful.NOTE From basic.start.static/2:
-- not dynamic initialization