是否保证constexpr(即常量初始化)模板变量的初始化顺序?

时间:2018-05-17 22:54:43

标签: c++ initialization global-variables language-lawyer constexpr

来自en.cppreference.com/w/cpp/language/initialization

  

无序动态初始化, [sic] 仅适用于(静态/线程局部)类模板静态数据成员和可明确专门化的变量模板(自C ++ 14)。

因此,静态模板似乎容易受到更糟糕的 The Static Initialization Order Fiasco (TSIOF) 版本的影响(即在翻译单元中无序)。

是否使用constexpr删除此漏洞?

即。以下代码保证的输出是success

显然,由于这个问题的性质,工作实例不足以作为答案;标准中的引用将是必需的。 (C ++ 17答案首选)

#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";
}
顺便说一句,如果有人是这方面的专家,我有一个相关的,未回答的问题(对于这样的专家来说很容易回答)here。 此外,如果my other question的答案是否定的(即constexpr对翻译单元没有帮助),那么会产生什么影响呢?

更新:我需要澄清我的担忧在这里。原始问题标题询问初始化顺序是否是constexpr模板变量的关注点。我澄清了。我并不担心示例中是否发生了动态初始化;事实并非如此。我担心的是,由于在动态初始化的情况下不能假设有序初始化,是否可以在常量初始化情况下假设?在看到动态初始化的模板变量的行为(在同一个翻译单元中)之前,我甚至都没想过这个。但是,由于动态初始化,静态持续时间模板变量不提供有序初始化,现在我没有理由认为常量初始化的静态持续时间模板变量也保证了有序初始化。我需要100%确定模板变量的常量初始化按照它们在TU中的定义顺序进行。

同样,我认为没有理由假设编译器中的常量初始化器需要按顺序初始化,如果动态初始化器不是。标准中没有警告,即常量初始化是无序的,这是不够的。

我意识到有些人可能认为这是一个过分关注的问题,但我正在研究安全关键软件,我的公司已经暂停采用C ++ 14,直到这个问题得到解决。

2 个答案:

答案 0 :(得分:2)

基于basic.start.static

  

如果是变量或临时的,则执行常量初始化   具有静态或线程存储持续时间的对象由a初始化   实体的常量初始值设定项。

在您的代码中:

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

正在进行常量初始化,这使得:

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

由于模板“常量评估而使用42初始化。

表明(来自expr.const/8.7):

  

一个变量,其名称显示为可能的常量计算值   表达式是 constexpr变量或非易失性   const限定的整数类型或引用类型。

因此,保证输出始终为"success" ful。

注意来自basic.start.static/2

  

一起调用零初始化常量初始化   静态初始化

- 动态初始化

答案 1 :(得分:0)

[请注意,在本文中,a<T>b<T>分别缩短为ab。]

[expr.const/3]指出:

  

如果变量是constexpr变量,或者是引用类型或const限定的整数或枚举类型,并且其初始化变量为,则遇到初始化声明后,该变量可在常量表达式中使用 。常量初始化程序。

因此,由于a的指定,bconstexpr的初始化期间将可用。 constexpr完全适用于变量和变量模板,如[dcl.constexpr/1]的第一句话所述。

  

constexpr说明符应仅应用于变量或变量模板的定义或函数或函数模板的声明。

因此,声明为constexpr的变量或变量模板在其自身初始化后可用于常量表达式(包括其他constexpr变量的初始化)中,并且其初始化中指定的值(由于常量) constexpr暗含保证初始化。)


如果我们希望探索[expr.const/3]中的第二个选项,而不是依靠“ constexpr表示是”子句,那么该子句也将导致a在{{1 }}的初始化,尽管它采用了更为circuit回的路线,其中包括深入研究标准的多个部分并将隐含的保证因素拼凑在一起。

首先,要问一个问题:b是否具有常量初始化程序。要确定它是否具有常量初始化程序,我们可以查阅[expr.const/2],其内容为(省略注释):

  

用于变量或临时对象的常量初始化器 o是一个初始化器,对于该初始化器,将其完整表达式解释为 constant-expression 会得到一个常量表达式,除了如果o是一个对象,则此类初始化程序还可以为o及其子对象调用constexpr构造函数,即使这些对象属于非文字类类型。

我们有两个明确的保证,即初始化程序的全表达式是一个常量表达式,均来自[dcl.constexpr/9]a是隐式a,而全表达式 not < / em>为常量表达式会出错。

  

在对象声明中使用的const说明符将对象声明为const。   这样的对象应具有文字类型并应进行初始化。   在任何constexpr变量声明中,初始化的完整表达式应为常量表达式。

作为扩展,这还意味着(从C ++ 14开始)constexpr进行零初始化([basic.start.static/2],无关的部分):

  如果使用常量初始化程序([expr.const])为实体初始化具有静态或线程存储持续时间的变量或临时对象,则会执行

常量初始化。   如果不执行常量初始化,则将具有静态存储持续时间([basic.stc.static])或线程存储持续时间([basic.stc.thread])的变量初始化为零([dcl.init])。

要验证a在初始化后是否具有正确的值,如果我们想真正地了解它们,我们可以查看[intro.execution/9]

  

在与要评估的下一个完整表达式相关联的每个值计算和副作用之前,对与完整表达式相关联的每个值计算和副作用进行排序。

我们知道a的初始化是一个完整表达式,我们隐式保证a在其完整表达式结束时将被赋值为a在评估序列中的下一个完整表达式(包括其他初始化)之前。通过将其与41[expr.const/3]结合使用,我们可以保证(与[basic.start.static/2]子句一样),constexpr可在初始化后遇到的常量表达式中使用,例如a的初始化,并保证b