У меня есть контейнер std::vector<std::map<int, std::unique_ptr<int>>> (если упростить). Изначально я должен вставить в std::vector некоторое количество std::map, у каждого из них будет по одной паре ключ-значение. Я пробовал такой код:
#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;
int main()
{
vector<map<int, std::unique_ptr<int>>> data{};
for (int x = 0; x < 10; x++)
{
data.emplace_back(std::make_pair(x, std::make_unique<int>(x)));
}
}
Но это не сработало. Как я могу изменить его, чтобы он работал так, как ожидалось?
Обновлено: я понял свою ошибку, мне пришлось использовать этот фрагмент кода, чтобы добавить элемент:
std::map<int, std::unique_ptr<int>> m;
m.emplace(x, std::make_unique<int>(y));
data.emplace_back(std::move(m));
Тогда код будет работать в онлайн-компиляторах, но по-прежнему не работает в Visual Studio.
Также stackoverflow.com/questions/1452721/…
Есть причина, по которой сильно вложенные монолитные структуры данных не рекомендуются. Они приводят к коду, который трудно написать и понять. Определите несколько простых классов, чтобы разбить структуру данных на именованные сущности. Это облегчит жизнь всем, в том числе и вам.
Я знаю, я не использую using namespace std; в своем проекте, это минимально воспроизводимый пример. Также пытался вставить так: data.emplace_back(map<int, std::unique_ptr<int>>{ std::make_pair(x, std::make_unique<int>(x)) }); но не получилось
Проблема заключается в построении карты без копирования типа значения, которое из-за unique_ptr не может быть скопировано. Как бы вам явно ни нравилось писать минимальный код, который делает очень много с очень небольшими затратами, в этом случае, я думаю, вам просто придется разбить код на более мелкие шаги.
«Изначально я должен вставить в std::vector некоторое количество std::map» — Ваши собственные слова: где в вашей программе есть код, который вообще создает какие-либо std::map? Как вы думаете, что std::map нужно вставить в вектор и почему? Можете ли вы показать код, который создаст такой std::map? Где код говорит std::make_pair(x, std::make_unique<int>(x)), как вы думаете, что за вещь создается этим кодом? Имеет ли смысл хранить это на карте? Что, если вы попытаетесь иметь карту и использовать что-то подобное для ввода данных в карту, а затем поместить карту в вектор?
«Но это не сработало. Как я могу изменить его, чтобы он работал как положено?» Это не ясный вопрос, потому что вы не описываете, что произошло, и вы не описываете, что вместо этого должно произойти. Это тоже бесполезный вопрос, потому что решить его вам мешает не недостаток знаний или понимания, а недостаток внимания и анализа.





Причина вашей ошибки в том, что emplace_back попытается создать map в vector, например:
std::map<int, std::unique_ptr<int>>(std::make_pair(...))
См. также: конструкторы std::map
Однако это невозможно, потому что конструктор, принимающий пары, принимает std::initializer_list<std::pair<K, V>>. Не существует конструктора, который принимает одну пару. Правильный способ:
map<int, unique_ptr>{std::make_pair(...)}
Однако конструкторы, использующие std::initializer_list<T>, требуют, чтобы T был копируемым, а std::unique_ptr<int> не копируемым, поэтому содержащий T = std::pair<int, std::unique_ptr<int>> также не копируемый. Это оставляет нам следующее решение:
#include <iostream>
#include <map>
#include <vector>
#include <memory>
int main()
{
using map_type = std::map<int, std::unique_ptr<int>>;
std::vector<map_type> data{};
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
map_type m;
m.emplace(x, std::make_unique<int>(y));
data.emplace_back(std::move(m));
}
}
}
Смотрите живой пример
В стандартной библиотеке MSVC невозможно использовать std::vector<std::map<int, std::unique_ptr<int>>. Причина в том, что из-за строгих гарантий исключений std::vector требует, чтобы тип его элемента был std::nothrow_move_constructible или копировальным.
Вы можете видеть, что в MSVC STL, std::map имеет не-noexcept конструктор перемещения:
map(map&& _Right) : _Mybase(_STD move(_Right)) {}
Вот быстрое и грязное решение этой проблемы:
template <typename K, typename V>
struct nothrow_map : std::map<K, V> {
nothrow_map() = default;
nothrow_map(nothrow_map&& other) noexcept : std::map<K, V>(std::move(other)) {}
};
// ...
using map_type = nothrow_map<int, std::unique_ptr<int>>;
См. также: std::move_if_noexcept (используется std::vector внутренне)
Прошу прощения, но этот код у меня не работает
Error C2280 'std::pair<const int,std::unique_ptr<int,std::default_delete<int>>>::pair(const std::pair<const int,std::unique_ptr<int,std::default_delete<int>>> &)': attempting to reference a deleted function. Я использую Visual Studio, С++ 20.
Это странно, потому что он работает в онлайн-компиляторах, а тот же код не работает в Visual Studio.
Хорошо, спасибо за объяснение. Но я все равно не вижу смысла вводить такое ограничение
@JanSchultke Хорошая работа, если бы я мог дать этому ответу более одного голоса, я бы это сделал.
@RomanLeshchuk ответ теперь включает грязное исправление. Он работает, наследуя от std::map и предоставляя производному типу nothrow_map конструктор перемещения noexcept. Это грязно, но вряд ли когда-нибудь выйдет из строя, поскольку в этом случае конструктор перемещения std::map выдаст только ошибку выделения, а это маловероятно.
Вам нужно скормить
std::map<int, std::unique_ptr<int>>вdata.emplace_back(...), а не паруintиunique_ptr<int>