Неправильно ли gcc разрешать инициализацию члена массива const с другой ссылкой на массив?

Во время (повторной) реализации простой карты 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?

Эм, кстати, что бы вы сделали, чтобы не копировать элементы массива вручную?

Вы не можете копировать элементы вручную, как только массив const создан, вы не можете его изменить.

Mark Ransom 12.02.2023 19:16

Я думаю, что gcc ошибся. Ни в коем случае вы не можете инициализировать такой массив.

Sam Varshavchik 12.02.2023 19:16

@MarkRansom Ну, ты прав, за это нужно удалить const.

MatG 12.02.2023 19:53
Руководство для начинающих по веб-разработке на React.js
Руководство для начинающих по веб-разработке на React.js
Веб-разработка - это захватывающая и постоянно меняющаяся область, которая постоянно развивается благодаря новым технологиям и тенденциям. Одним из...
Разница между Angular и React
Разница между Angular и React
React и AngularJS - это два самых популярных фреймворка для веб-разработки. Оба фреймворка имеют свои уникальные особенности и преимущества, которые...
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
7
3
745
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я непреднамеренно использую здесь некоторые нестандартные расширения 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 не соответствует требованиям.

Другие вопросы по теме