Перейти к содержанию

[C++] Ошибка конструктора многомерного массива


Рекомендуемые сообщения

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

Потихоньку открываю для себя мир C++ и проходя курсы пишу небольшие тесты.

И вот на составлении исходных данных для теста я не смог создать многомерный массив, компилятор очень ругается. Никак не возьму в толк почему.

 

Теперь подробнее. Я создаю вектор массивов векторов и компилятор падает, хотя по мне всё выглядит правильно:

vector<array<vector<int>, 2>> cases = {
    {{1, 5, 3, 4, 2}, {2, 4, 3, 5, 1}},
    {{1, 2, 3}, {3, 2, 1}},
    {{1, 2}, {2, 1}},
    {{1}, {1}},
    {{}, {}},
};

 

Чтобы компилятор не падал, пришлось добавить по скобке для каждой строки, но я в упор не понимаю зачем. Вот это компилируется:

vector<array<vector<int>, 2>> cases = {
    {{{1, 5, 3, 4, 2}, {2, 4, 3, 5, 1}}},
    {{{1, 2, 3}, {3, 2, 1}}},
    {{{1, 2}, {2, 1}}},
    {{{1}, {1}}},
    {{{}, {}}},
};

Но тогда тут получается 4 измерения, как по мне, а не 3. Потому что если разложить по измерениям я прихожу к своей реализации:

// Одномерный вектор
vector<int> cases = {1, 2, 3, 4, 5};

// Превращаем его в вектор массивов
vector<array<int>> cases = {{1, 2}, {3, 4}};

// Превращаем его в вектор массивов векторов (перестает компилироваться!)
vector<array<vector<int>, 2>> cases = {{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}};

И вроде всё верно, три измерения, но падает с ошибкой 

 error: could not convert 
   '{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}}' 
 from '<brace-enclosed initializer list>' to 'std::vector<std::array<std::vector<int>, 2ul> >'
 vector<array<vector<int>, 2>> x = {{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}};

 

Причем по ошибке не могу нагуглить именно свой случай. Кто-нибудь сталкивался с подобным? Есть какое-то логическое объяснение, почему нужно оборачивать дополнительной скобкой?

 

Если непонятно зачем это всё, я гоняю вот такие тестики, чисто для себя:

Спойлер

for (auto c : cases) {
    auto v = c[0];
    auto e = c[1];

    vector<int> r = TestiruemayaFunction(v);
    if (r != e) {
        cout << "Failed on ";
        for (auto i : v) {
            cout << i << ' ';
        }
        cout << ": ";
        for (auto i : r) {
            cout << i << ' ';
        }
        cout << " != ";
        for (auto i : e) {
            cout << i << ' ';
        }
        return;
    }
}

 

Ссылка на комментарий
Поделиться на другие сайты

В общем из-за того что ты создаешь контейнер тебе придется все облачать в скобки, а по скобкам:

 

vector<array<vector<int>, 2>>

 

vector{array<vector<int>, 2>{vector<int>, 2{}}} 

...........1...................................2.....................3.......

 

array[0] = {{1, 5, 3, 4, 2}, {2, 4, 3, 5, 1}}
array[1] = {{1, 2, 3}, {3, 2, 1}}

 

Если по простому, то под каждый контейнер нужны скобочки как в доках. Почему именно так, а не иначе, на это смогут ответить лишь спецификации C++.

 

Ссылка на комментарий
Поделиться на другие сайты

41 минуту назад, partoftheworlD сказал:

Если по простому, то под каждый контейнер нужны скобочки как в доках.

 

Ну вот, ты нарисовал по 3 блока скобок. А получается нужно 4, иначе не компилируется.

Если разбить на переменные то не нужно:

vector<int> x = {1,2,3};
vector<int> y = {4,5,6};
array<vector<int>,2> a = {x, y};
vector<array<vector<int>,2>> r = {a};

Но тогда если раскрывать обратно, то оно не компилируется:

Спойлер

// Начальный вариант с переменными
vector<int> x = {1,2,3};
vector<int> y = {4,5,6};
array<vector<int>,2> a = {x, y};
vector<array<vector<int>,2>> r = {a};

// Раскрываем a
vector<int> x = {1,2,3};
vector<int> y = {4,5,6};
vector<array<vector<int>,2>> r = {{x, y}};

// Раскрываем x и y (перестает компилироваться)
vector<array<vector<int>,2>> r = {{{1, 2, 3}, {4, 5, 6}}};

 

И вот я не очень понимаю, зачем нужно писать {{{{1, 2, 3}, {4, 5, 6}}}} вместо {{{1, 2, 3}, {4, 5, 6}}} , ведь в этой записи видно 3 измерения и всё выглядит корректно.

Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, srg91 сказал:

vector<array<vector<int>,2>> r = {{x, y}};

Хм... интересненькая задача))) Правда у меня студия уже тут начинает матерится, на отсутсвие экземпляра конструктора, для соответствующего списка аргументов. 

Правда я особо не использовал vector, а чаще обходился обычными массивами. Сейчас попробую покапаться,  может что-то и найду :))

 

Блин, думал, что разобрался, уже расписал ответ, и уже по нему понял, что нет :(

Приложу скрин того, как это видит студия, дабы еще больше нас запутать :D

 

https://drive.google.com/open?id=1OnRtK1surpqcFF_hM7OLqZkwQ4SPGkN3

 

Судя по скрину студия так же видит. 

{НашКонтенер{Масив{Элемент масива}}}

 

Если ответ найдется, отпишите тут, а то мне тоже интересно :)

 

Изменено пользователем temtriss
Ссылка на комментарий
Поделиться на другие сайты

1 час назад, temtriss сказал:

Блин, думал, что разобрался, уже расписал ответ, и уже по нему понял, что нет :(

Уже разобрались, дело было в std::array и его агрегатной инициализации, у него внешние фигурные скобки агрегатный синтаксис инициализации, а внутренние - синтаксис инициализатора массива. Ну как разобрались, понятно в чем дело, но не понятно как это работает.:D

 

Спойлер
Цитата

std::array<T, N> - это агрегат: у него нет никаких объявленных пользователем конструкторов, даже если вы не принимаете std::initializer_list . Инициализация с использованием фигурных скобок выполняется с использованием агрегатной инициализации , функции C ++, которая была унаследована от C. «Старый стиль» агрегатной инициализации использует: std::array<int, 4> y = { { 1, 2, 3, 4 } }; В этом старом стиле инициализации агрегата можно выполнить дополнительные фигурные скобки, поэтому это эквивалентно: std::array<int, 4> y = { 1, 2, 3, 4 }; Однако эти дополнительные фигурные скобки могут быть отменены только «в объявлении формы T x = { a }; » (C ++ 11 §8.5.1 / 11), то есть когда используется старый стиль = . Это правило, разрешающее выравнивание фигур, не применяется для прямой инициализации списка. В сноске здесь говорится: «Скобки не могут быть исключены при других использованиях инициализации списка». Существует сообщение о дефекте, касающееся этого ограничения: дефект CWG № 1270 . Если предлагаемая резолюция будет принята, для других форм инициализации списка будет разрешено исключение фигурных скобок, и следующее будет хорошо сформировано: std::array<int, 4> y{ 1, 2, 3, 4 };

 

 

  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

Только что, partoftheworlD сказал:

Уже разобрались, дело было в std::array и его агрегатной инициализации, внешние фигурные скобки агрегатный синтаксис инициализации, а внутренний - синтаксис инициализатора массива

 

Ну не до конца всё же.

Потому что я пока еще не понимаю, почему для случая vector<array<int, 2>> v = {{1, 2}, {3, 4}}; не используется агрегатный синтаксис, а для случая что я приводил выше - используется :)

 

Ссылка на комментарий
Поделиться на другие сайты

2 минуты назад, srg91 сказал:

Потому что я пока еще не понимаю

Никто не понимает, кроме тех кто писал стандарт мне так кажется (я уже с этим смерился):D 

 

Ссылка на комментарий
Поделиться на другие сайты

Может так

Screenshot_1.png

 

Предполагаю, что там где описание массива из векторов —там кажется что скобки лишние. Двойка в описании массива означает инициализацию двух элементов, которые можно заполнить в массиве после знака "=", что и происходит

 

 

Ссылка на комментарий
Поделиться на другие сайты

1 минуту назад, MasterGH сказал:

Предполагаю

 

Ты не очень правильно выделил красным, красное - это вот эти два раздельных вектора:

Спойлер

vector.png

 

А синим действительно это инициализация массива, только почему-то с использованием агрегатного синтаксиса. Осталось понять, почему в случае если массив содержит вектора - используется агрегатный синтаксис. А если содержит числа например, то не агрегатный.

 

10 минут назад, partoftheworlD сказал:

Никто не понимает, кроме тех кто писал стандарт мне так кажется (я уже с этим смерился):D 

 

Точно, а компилятор на самом деле отправляет сигнал в космос, в котором он пролетая сквозь хаскелевые экзопланеты балуется монадами и находит ответ :)

 

  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

8 часов назад, srg91 сказал:

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

1 час назад, srg91 сказал:

Осталось понять, почему в случае если массив содержит вектора - используется агрегатный синтаксис. А если содержит числа например, то не агрегатный.

 

Как я помню. Вектор — класс, массив — структура. В первом случае будут поинтеры, во втором адреса данных. Может быть разные типы данных связаны с дополнительным блоком скобок.

 

@srg91, если есть возможность, то мог бы в CE DessectData развернутую иерархию данных заскринить. Было бы интересно посмотреть на поинтеры

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, MasterGH сказал:

если есть возможность

Ну, если очень мельком глянуть (я попытался посмотреть структуру вектора в исходниках, но у меня не получилось :)), то выглядит следующий код выглядит примерно так:

vector<int> x = { 1,2,3 };
vector<int> y = { 4,5,6 };
array<vector<int>, 2> a = { x, y };
vector<array<vector<int>, 2>> r = { a };
Спойлер

vector2.png

 

где красное - вектора x и y, синее массив a их объединяющий и оранжевый - общий вектор r.

Да, массив, судя по исходникам, является классом, но в памяти выглядит как подряд идущие объекты, поэтому вложенные в него вектора идут ровно подряд. 

Сам вектор похоже состоит из 4-х указателей, 3 из которых я так и не понял что, а вот второй - ссылка на текущий массив данных.

  • Понравилось 1
Ссылка на комментарий
Поделиться на другие сайты

проще было бы использовать тогда уже vector а не array

а облачать еще в скобки нужно потому что это уже не просто данные массива

упрощенно array это template<class Ty, size_t Size> struct {Ty[Size] data;}

а подобные простые классы или структуры (или union) можно инициализировать упрощенно

у меня уже тут ошибку дает

std::vector<std::array<int, 2>> data2 = { { 1, 2 } };

а вот так не дает

std::vector<std::vector<int>> data = { { 1, 1 } };

можно присваивать, как ты присвоил a

с vector of array не пашет упрощение, потому что список инициализации для

вот тут про это говорится

https://stackoverflow.com/questions/6041459/c-vector-of-arrays

из-за того что это уже не просто array, а vector<array> правило для упрощения инициализации не работает

можно еще тут прочесть (не сказать что я все уяснил)) )

http://ru.cppreference.com/w/cpp/container/array

http://ru.cppreference.com/w/cpp/container/vector

http://ru.cppreference.com/w/cpp/language/aggregate_initialization

http://ru.cppreference.com/w/cpp/language/list_initialization

(хотя кажется лучше en версию читать)) )

 

чтобы понять как он хранится в памяти проще в ide дебаг запустить и посомтреть locals

(у vector есть allocator,allocator_vals,first,last)

  • Понравилось 2
  • Плюс 2
Ссылка на комментарий
Поделиться на другие сайты

15 минут назад, X86Jumps сказал:

вот тут про это говорится

 

Спасибо, очень похоже на то. Если я правильно понял из ответа на stackoverflow, то из-за строения array мы должны инициализировать его и одновременно инициализировать его внутренний _Ty _Elems[_Size];

И, получается, по хорошему массивы всегда должны задаваться с двумя скобками.

Но при этом есть "специальное правило", которое позволяет "опускать" лишние скобки в случае инициализации агрегатов, но только с условием что это "простая" инициализация, по типу std::vector<std::array<int, 2>>. Если массив содержит что-то, что нужно дополнительно инициализировать, скобки обязательны.

Поэтому собственно с array<int, 2> работает, а с array<vector..., 2> нет.

 

Кажется что-то стало более менее понятно, спасибо большое! :)

  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

В 26.04.2018 в 23:52, X86Jumps сказал:

у меня уже тут ошибку дает

 

У меня так же было)) Для устранения #include <array>

Да у меня тоже студия видила std::array, но ругалась на какую-то хрень :) Подключил array перестала ошибка вылезать))

 

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, temtriss сказал:

У меня так же было)) Для устранения #include <array>

ошибка правильно выходила, эта не баг

выше я описал причину. Без #include <array> я бы даже не смог использовать этот контейнер из stl (хотя он еще в tr)

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.