【问题标题】:MSVC 2017 violating static initialization order within single translation unitMSVC 2017 在单个翻译单元中违反静态初始化顺序
【发布时间】:2018-05-18 01:56:47
【问题描述】:

MSVC 2017 社区 -std=c++17 阻塞了以下示例:

#include <iostream>

struct TC
{
    static TC const values[];
    static TC const& A;
    static TC const& B;
    static TC const& C;

    int const _value;
};

inline constexpr TC const TC::values[]{ { 42 }, { 43 }, { 44 } };
inline constexpr TC const& TC::A{ values[0U] };
inline constexpr TC const& TC::B{ values[1U] };
inline constexpr TC const& TC::C{ values[2U] };

int main(int, char**) noexcept
{
    std::cout << std::boolalpha
        << "&A == &values[0]? " << (&TC::A == &TC::values[0U]) << "\n" 
        << "&B == &values[1]? " << (&TC::B == &TC::values[1U]) << "\n"
        << "&C == &values[2]? " << (&TC::C == &TC::values[2U]) << "\n";
    return 0;
}

预期的输出是:

&A == &values[0]? true
&B == &values[1]? true
&C == &values[2]? true

gcc 和 clang 都产生了,但 MSVC 给出了:

&A == &values[0]? true
&B == &values[1]? false
&C == &values[2]? false

如果_value 成员被删除并且没有用户定义的构造函数,MSVC 会给出正确的结果。

由于所有这些都在一个翻译单元中,我的理解是这属于Partially-ordered dynamic initialization

2) 部分有序的动态初始化,适用于所有 不是隐式或显式实例化的内联变量 专业化。如果在有序或之前定义了一个部分有序的 V 每个翻译单元中的部分有序 W,V 的初始化 在 W 的初始化之前排序(或者发生在之前,如果 程序启动一个线程)

我无法使用函数来确保初始化顺序,因为我需要 constexpr constexpr 对它们的引用。

所以问题是,MSVC 在这里违反了标准*,对吗?

*当然cppreference.com 不是“标准”,但我假设那里的信息来源正确。

【问题讨论】:

  • 从什么时候开始可以合法地将一个对象声明为非 constexpr 然后将其定义为 constexpr..?
  • 听起来像是要提交给 MS 的反馈(在 VS 的帮助菜单下 -> 提交反馈)或者 developercommunity.visualstudio.com/spaces/62/index.html(它曾经是 connect.microsoft.com,但显然已经停止了)。
  • @ildjarn 我在标准中看不到任何禁止这种用法的内容
  • @ildjarn Richard Smith cites the standard - “7.1.5/1 说'constexpr 说明符应仅应用于变量的定义......'”所以constexpr(显然)是声明中不需要。
  • @monkey_05_06 在该引用的“...”中,它继续列出可以应用constexpr 的其他地方,例如声明。这句话也来自 C++11。

标签: c++ visual-c++ reference constexpr static-initialization


【解决方案1】:

我将同样的问题隔离到这个更简单的例子中:

#include <iostream>

int values[3];
constexpr int& v{ values[1] };

int main()
{
    std::cout << &v << ", " << &values[1] << "\n";
    return 0;
}

使用最新的 MSVC 2017 社区,提供以下输出:

0119D035, 0119D038

如果删除constexpr,则不会发生此问题。

所以我认为这是constexpr 引用初始化的编译器错误。引用被初始化为&amp;values[0] + 1 字节,而不是应有的&amp;values[1]

注意。如果有人不熟悉 constexpr 参考定义,see herehereconstexpr 强制初始化器是一个具有静态存储持续时间的对象。

【讨论】:

  • 感谢您的确认!我会给它几天时间看看是否有其他人要添加任何内容,否则我会将其标记为已接受的答案并在缺陷报告中引用它。干杯!
  • 不出所料,MSVC 仍然会阻塞从 &amp;values[0] + 1std::addressof(values[0]) + 1 这样的表达式进行初始化,但如果我们在指针上使用一点类型双关语,它实际上可以正常工作。 reinterpret_cast 失败(应该如此),但 MSVC 和 gcc 仍然允许 *static_cast&lt;int const*&gt;(static_cast&lt;void const*&gt;(static_cast&lt;char const*&gt;(static_cast&lt;void const*&gt;(&amp;values[0])) + (sizeof(int) * 2))),因此,MSVC 实际上将引用绑定到正确的对象(就像 gcc 一样)。 clang 不允许这种废话。
  • 我扩展了您的测试用例 here(我找不到 MSVC C++17 的在线编译器)。 GCC 给出了完全预期的输出,但 MSVC 2017 社区显示,对于项目 IntArray[n] 的引用,每个引用都完全偏离了 3*n 字节。
  • 经过大量试验和错误,试图弄清楚微软开发者社区论坛的代码格式(专业提示:没有),我选择了post a screenshot instead。他们的论坛也不(显然)允许超链接,但我引用了这个 SO 问题的问题编号。
猜你喜欢
  • 1970-01-01
  • 2019-05-21
  • 1970-01-01
  • 2016-02-16
  • 2010-11-16
  • 2021-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多