Во время (повторной) реализации простой карты constexpr я написал это (божественная стрела):
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 в качестве компилятора, и все вроде бы работало хорошо. Когда я решил попробовать это с msvc и clang, у меня были ошибки компиляции: оба жаловались на инициализацию массива, требующую списка инициализаторов, заключенного в фигурные скобки.
Оглядываясь назад, другие компиляторы не особенно ошибаются, не так ли? Я непреднамеренно использую здесь некоторые нестандартные расширения gcc?
Эм, кстати, что бы вы сделали, чтобы не копировать элементы массива вручную?
Я думаю, что gcc ошибся. Ни в коем случае вы не можете инициализировать такой массив.
@MarkRansom Ну, ты прав, за это нужно удалить const.
Я непреднамеренно использую здесь некоторые нестандартные расширения 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>{})
{}
[class.base.init]/7:
Список-выражений или список-инициалов в скобках в mem-initializer используется для инициализации назначенного подобъекта (или, в случае делегирующего конструктора, полного объекта класса) в соответствии с правилами инициализации [dcl.init] для прямая инициализация.
[dcl.init.general]/16.5:
В противном случае, если целевой тип является массивом, объект инициализируется следующим образом. Пусть x1, …, xk — элементы списка-выражения. Если целевой тип представляет собой массив с неизвестной границей, он определяется как содержащий k элементов. Пусть n обозначает размер массива после этой потенциальной корректировки. Если k больше n, программа некорректна. В противном случае элемент массива ith инициализируется копированием с помощью xi для каждого 1 ≤ i ≤ k и инициализируется значением для каждого k < i ≤ n. Для каждого 1 ≤ i < j ≤ n каждое вычисление значения и побочный эффект, связанные с инициализацией элемента ith массива, располагаются перед теми, которые связаны с инициализацией элемента jth.
Пункт 16.5 имеет приоритет перед всеми последующими пунктами, в частности теми, которые охватывают инициализацию копированием из значения того же типа (16.6 и 16.9). Таким образом, элемент данных массива может быть инициализирован только путем индивидуальной инициализации каждого из элементов массива. Таким образом, поведение GCC не соответствует требованиям.
Вы не можете копировать элементы вручную, как только массив const создан, вы не можете его изменить.