c++ gcc允许用另一个数组引用初始化const数组成员是错误的吗?

zpgglvta  于 2023-02-14  发布在  其他
关注(0)|答案(2)|浏览(110)

在(重新)实现一个简单的constexprMap时,我编写了以下代码(godbolt):

template <class key_type, class value_type, int N>
class flat_map
{
private:
    struct pair
    {
        key_type key;
        value_type value;
    };
    const pair elements[N];

public:
    consteval flat_map(const pair (&arr)[N]) noexcept
        : elements(arr) // works on gcc?!
    {}

    [[nodiscard]] consteval value_type operator[](const key_type key) const
    {
        for (const pair &elem : elements)
            if (elem.key == key)
                return elem.value;
        throw "Key not found";
    }
};

constexpr flat_map<int, char, 3> m = {{
    { 4, 'a' }, { -1, 'b' }, { 42, 'c' }
}};
static_assert(m[4] == 'a');
static_assert(m[-1] == 'b');
static_assert(m[42] == 'c');

int main()
{
    return m[4]; // 97=='a'
}

我天真地想到将私有数组elements设置为const并在构造函数中初始化它;我使用 gcc trunk作为编译器,看起来一切都很好。当我决定使用 msvcclang 时,我遇到了编译错误:两者都抱怨数组初始化需要用大括号括起来的初始化器列表。
事后看来,其他的编译器并没有特别错,是吗?我是不是无意中使用了一些 gcc 非标准扩展?
嗯,顺便问一下,你会怎么做来避免手工复制数组元素?

vfhzx4xs

vfhzx4xs1#

[类.基.初始化]/7:

  • mem-initializer* 中的 expression-listbraced-init-list 用于根据直接初始化的[dcl.init]初始化规则初始化指定的子对象(或者在委托构造函数的情况下,初始化整个类对象)。

[文件首字母缩写:一般]/16.5:
否则,如果目标类型是数组,则按如下方式初始化对象。令 x1,...,x**kexpression-list 的元素。如果目标类型是具有未知边界的数组,则将其定义为具有 k 个元素。令 n 表示此潜在调整后的数组大小。如果 k 大于 n,则程序为格式错误。否则,对于每个1 ≤ ik,用 x**i 复制初始化第 i 个数组元素,并且对于每个 kin 进行值初始化。对于每个1 ≤ ijn,与数组的第 i 个元素的初始化相关联的每个值计算和副作用被排序在与第 j 个元素的初始化相关联的那些值计算和副作用之前。
第16.5点优先于后面的所有点,特别是那些涉及从相同类型的值复制初始化的点(16.6和16.9)。因此,数组数据成员只能通过单独初始化数组的每个元素来初始化。GCC的行为因此是不符合的。

k7fdbhmy

k7fdbhmy2#

我是否无意中使用了一些gcc非标准扩展?
是的......我想gcc使用的是非标准扩展
嗯,顺便问一下,你会怎么做来避免手工复制数组元素?
为什么要避免呢?使用委托构造函数很简单。
您可以使用以下内容替换构造函数

template <std::size_t ... Is>
consteval flat_map(const pair (&arr)[N],
                   std::index_sequence<Is...> const &) noexcept
  : elements{ arr[Is]... }
   {}

consteval flat_map(const pair (&arr)[N]) noexcept
  : flat_map(arr, std::make_index_sequence<N>{})
   {}

相关问题