Is the initialization order of constexpr (i.e. con

2020-07-11 06:09发布

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.

2条回答
何必那么认真
2楼-- · 2020-07-11 06:39

[Note that throughout this post, a<T> and b<T> are shortened to a and b, respectively.]

[expr.const/3] states that:

A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.

Thus, a will be usable during b's initialisation, due to the constexpr specifier. constexpr is fully applicable both for variables and variable templates, as stated in the first sentence of [dcl.constexpr/1].

The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.

Thus, a variable or variable template declared constexpr is usable in constant expressions (including the initialisation of other constexpr variables) after its own initialisation, with the value specified in its initialisation (due to constant initialisation being implictly guaranteed by constexpr).


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 to a being usable during b'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):

A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

We have two explicit guarantees that the initialiser's full-expression is a constant expression, both from [dcl.constexpr/9]: a is implicitly const, and the full-expression not being a constant expression would be an error.

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

This, by extension, also means that (as of C++14), a will not be subject zero initialisation ([basic.start.static/2], irrelevant parts omitted):

Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer ([expr.const]) for the entity. If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is zero-initialized ([dcl.init]).

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]:

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

As we know that a's initialisation is a full-expression, we are implicitly guaranteed that a will have been assigned the value 41 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 the constexpr clause), a is usable in constant expressions encountered after its own initialisation, such as b's initialisation, and also guaranteed that a == 41 && a != 0.

查看更多
三岁会撩人
3楼-- · 2020-07-11 06:48

Based on basic.start.static:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer for the entity.

In your code:

template<class T> static constexpr T a = 41; // constant initialization

is doing constant initialization which makes:

template<class T> static constexpr T b = a<T>+1;

be initialized with 42 due to the templates' constant evaluation.

which states that (From expr.const/8.7):

a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.

Thus, it is guaranteed the output is always "success"ful.

NOTE From basic.start.static/2:

Together, zero-initialization and constant initialization are called static initialization

-- not dynamic initialization

查看更多
登录 后发表回答