数组类型的复制初始化

303*_*303 3 c++ language-lawyer c++20

考虑下面的示例,其中t声明为int[1]; 根据 C++20 标准,ain的初始化合法吗?t a = t{};初始化的行为t a = t{};是否会受到标记为的a影响constexpr?C++20 标准对此有何规定?那么 的初始化又如何呢constexpr auto a = t{};?为什么 MSVC 接受它?

static_assert([]{
    using t = int[1];
    constexpr t a{};        // all ok
    constexpr t a = {};     // all ok
    constexpr t a = t{};    // clang nope, gcc ok, msvc ok
    t a = t{};              // clang nope, gcc ok, msvc nope
    constexpr auto a = t{}; // clang nope, gcc nope, msvc ok
    return true;
}());
Run Code Online (Sandbox Code Playgroud)

实例


来自 Clang 的错误消息:

<source>:4:7: error: array initializer must be an initializer list
    4 |     t a = t{};
      |       ^
Run Code Online (Sandbox Code Playgroud)

来自 GCC 的错误消息:

<source>:4:24: error: taking address of temporary array
    4 |     constexpr auto a = t{};
      |                        ^~~
Run Code Online (Sandbox Code Playgroud)

MSVC 的错误消息:

<source>(4): error C2075: 'a': initialization requires a brace-enclosed
initializer list
Run Code Online (Sandbox Code Playgroud)

use*_*522 6

在 C++17 中constexpr t a = t{};t a = t{};都是格式错误的,因为 的初始化一直到[dcl.init]/17.5a (草案 N4659)为止,这实际上要求数组(除了通过字符串文字初始化字符数组之外)可以只能由or或形式的初始值设定项进行初始化,其中是可选的以逗号分隔的初始值设定项列表。这些都不匹配。{/*...*/}= {/*...*/}()/*...*/t{}

在 C++20 中,添加了来自括号的聚合初始化,因此(/*...*/)允许使用以下形式的初始值设定项来初始化数组。因此,初始化规则中的失败情况 17.5 被替换为带括号的聚合初始化规则(请参阅草案 N4868 中的[dcl.init.general]/16.5)。不幸的是,新的措辞假设如果到达项目符号,则数组初始值设定项必须具有 form (/*...*/),忽略问题中与t{}任何形式不匹配的情况。

因此 C++20 标准缺乏针对这些情况的规范。

CWG 问题 2824已被报告,尽管它涉及初始化程序是字符串文字而不是强制转换表达式且数组不是字符数组的特殊情况。无论如何,标记为“暂时就绪”并得到 CWG 批准的决议将使所有剩余的初始化程序形式再次变得格式错误,如 C++17 中那样。

因此,一旦这是正确的缺陷报告并由编译器供应商实现,则 和constexpr t a = t{};t a = t{};应该是格式错误的。


constexpr auto a = t{};格式错误,因为初始化不是常量表达式。您正在尝试将指针存储到从 实现的临时数组中t{}根据[expr.const]/11.2 ,这不是常量表达式允许的结果。

没有constexpr它应该没问题,但是初始化后指针立即悬空,因为临时数组再次被销毁。但是,初始化本身应该是允许的,因为[conv.array]指定数组到指针的转换也适用于数组右值。