【问题标题】:Should constexpr initialization happen before other initializationconstexpr 初始化是否应该在其他初始化之前发生
【发布时间】:2015-07-17 04:08:39
【问题描述】:

我有以下代码,它在 gcc 和 clang 上的行为与预期一致。但是,MSVC 给了我一个意想不到的结果。

让我们先看看有问题的代码。

#include <iostream>

// -----------------------------------------------

class Test // Dummy for MCVE
{
public:
    Test();
    void Print();
private:
    int arr[5];
};

Test tst;

// -----------------------------------------------

template<typename T>
struct range // some stuff not needed by example removed
{
    constexpr range(T n) : b(0), e(n) {}
    constexpr range(T b, T e) : b(b), e(e) {}
    struct iterator
    {
        T operator*() { return i; }
        iterator& operator++() { ++i; return *this; }
        bool operator!=(iterator other) { return i != other.i ; }
        T i;
    };
    iterator begin() const { return{ b }; }
    iterator end() const { return{ e }; }
private:
    T b,e;
};

constexpr range<int> coord(5);

// -----------------------------------------------

Test::Test()
{
    for(auto i : coord)
        arr[i]=i;
}

void Test::Print()
{
    for(auto i : coord)
        std::cout << arr[i] << std::endl;
}

// -----------------------------------------------

int main()
{
    tst.Print();
}


现在,在 clang 和 gcc 上都会打印 '0 1 2 3 4'
但是,在 MSVC 上,这会打印 '0 0 0 0 0'
原因是当构造函数对全局变量tst 运行时,'coord' 尚未初始化(到 0,5),但它也不是随机的,而是 (0,0)。
对我来说,constexpr 初始化发生在常规初始化之前是有意义的。这种行为是否符合 MSVC?

我可能应该注意到我使用的是 MSVC 版本 14.0.22823.1,并且 可以通过改变顺序得到预期的结果 声明

【问题讨论】:

    标签: c++


    【解决方案1】:

    对于静态存储持续时间对象的初始化必须按以下顺序进行:

    1. 零初始化。
    2. 不断初始化(即constexpr)。
    3. 动态初始化。

    相关标准,

    C++14 §3.6.2/2:

    具有静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 的变量应为零初始化 (8.5) 在任何其他初始化发生之前。 [...] 持续初始化 被执行:[...] 如果一个具有静态或线程存储持续时间的对象被构​​造函数调用初始化,并且如果 初始化全表达式是对象的常量初始化器; […] 零初始化和常量初始化一起称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。

    同一段定义(打断了文字的流动,所以我在上面去掉了)

    对象o常量初始化器 是一个常量表达式,除了它还可以为o 调用constexpr 构造函数及其子对象,即使这些对象属于非文字类类型。


    在我使用 Visual C++ 2015 验证的报告示例中,静态存储持续时间对象 tst 的动态初始化发生在静态存储持续时间对象 coord 的常量初始化之前,这是一个编译器错误。

    【讨论】:

    • According to B. Stroustrup, constexpr 的主要功能是扩展编译时可以计算的范围,使这种计算类型安全。声明为 constexpr 的对象在编译时评估其初始化程序;它们基本上是保存在编译器表中的值,仅在需要时才发送到生成的代码中。这似乎与标准所说的不同。
    • @RSahu:我认为没有冲突。考虑一个静态变量int const x = 3。这可以用作例如原始数组大小,所以它的编译时间非常长。同样,如果获取该变量的地址,则查看内存内容最好有一个3。它必须在加载程序之后和执行检查语句之前的某个时间放置在那里。关于初始化的标准术语是关于每种初始化发生的确切时间。
    • 现在说得通了。感谢您的澄清。在 OP 的用例中,由于 range 对象 coord 用于进行函数调用,因此它也需要在运行时进行初始化。
    • 注意,这里的规则有changed a bit recently。虽然这并没有真正改变这是一个错误。
    • 此编译器错误已在 VS 2015 Update 3 中修复(cl.exe 版本 19.00.24213.1,VS 版本 14.0.25425.01)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    相关资源
    最近更新 更多