Перейти к содержанию
Авторизация  
srg91

[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)

Поделиться сообщением


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

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация  

×

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

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