Mat*_*atG 7 c++ arrays member-initialization c++20 consteval
在(重新)实现一个简单的 constexpr 映射时,我写了这个(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'
}
Run Code Online (Sandbox Code Playgroud)
我天真地认为将私有数组设置elements为const并在构造函数中初始化它;我使用gcc trunk 作为编译器,一切似乎都运行良好。当我决定使用msvc和clang尝试它时,我遇到了编译错误:两者都在抱怨数组初始化需要大括号括起来的初始值设定项列表。
事后看来,其他编译器并没有特别错误,不是吗?我是否无意中使用了一些gcc非标准扩展?
嗯,顺便问一下,您会如何避免手动复制数组元素?
\n\n\nmem-initializer中的表达式列表或花括号初始化列表用于根据 [dcl.init] 的初始化规则初始化指定的子对象(或者,在委托构造函数的情况下,初始化完整的类对象)直接初始化。
\n
\n\n否则,如果目标类型是数组,则对象将按如下方式初始化。令x 1 , \xe2\x80\xa6, x k为表达式列表的元素。如果目标类型是未知边界的数组,则它被定义为具有k 个元素。令n表示此潜在调整后的数组大小。如果k大于n,则程序格式错误。否则,对于每个 1 \xe2\x89\xa4 i \xe2\x89\xa4 k ,第i个数组元素使用x i进行复制初始化,并且对于每个k < i \xe2\x89\xa4 n进行值初始化。对于每个 1 \xe2\x89\xa4 i < j \xe2\x89\xa4 n ,与数组的第 i个元素的初始化相关的每个值计算和副作用都在与第 j个元素的初始化相关的计算和副作用之前排序。元素。
\n
第 16.5 点优先于随后的所有点,特别是那些涵盖相同类型值的复制初始化的点(16.6 和 16.9)。因此,只能通过单独初始化 array\xe2\x80\x99s 中的每个元素来初始化数组数据成员。因此,GCC\xe2\x80\x99s 的行为是不合格的。
\n我是否无意中使用了一些 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>{})
{}
Run Code Online (Sandbox Code Playgroud)