C ++: как инициализировать вектор в карте с ненулевым размером - PullRequest
0 голосов
/ 17 ноября 2018

У меня есть карта векторов:

std::map<int, std::vector<bool>> mymap

Иногда мне нужно вставить новый элемент:

auto& newvec = mymap[42];
// Add stuff to newvec

Насколько я понимаю (и при условии, что 42 еще не на карте), это даст мне newvec с длиной 0 (построенной как std::vector<bool> {}), которую я затем смогу расширить.

Есть ли способ инициализировать вектор с некоторым размером n сразу?

(меня не беспокоит производительность, просто интересно, есть ли способ сделать это).

Ответы [ 3 ]

0 голосов
/ 17 ноября 2018

Вы можете использовать map :: emplace функцию-член:

mymap.emplace(42, std::vector<bool>(125, false));

, чтобы создать значение std::vector<bool>(125, false) для ключа 42.

As.Отметим, что вышеуказанный вызов emplace создаст значение std::vector<bool>(125, false), даже если ключ 42 уже существует на карте (это также задокументировано на странице cppreference, которую я связал выше).Если этого следует избежать, вы можете сначала проверить, существует ли уже значение, используя map :: find , и вставить значение, только если ключ не существует.То есть:

if (mymap.find(42) == mymap.end()) {
    mymap.emplace(42, std::vector<bool>(125, false));
}

Оба map :: find и map :: emplace имеют сложность логарифмического времени;следовательно, вызов find перед emplace не должен слишком сильно влиять на производительность в критических сценариях.

0 голосов
/ 17 ноября 2018

Используйте map::try_emplace() (или map::emplace() до C ++ 17)

std :: vector имеет конструктор, который принимает начальный размер и начальное унифицированное значение. В вашем случае предположим, что вы хотите 125 в качестве начального размера. С отдельным вектором вы должны использовать:

size_t num_bools_we_want = 1234;
std::vector<bool> my_vec(num_bools_we_want, false);

Теперь у std::map есть метод с именем map::try_emplace(), который перенаправляет аргументы в конструктор типа значения, что эффективно позволяет вам выбрать конструктор, который он будет использовать для нового элемента. Вот как это использовать

mymap.try_emplace(42, num_bools_we_want, false);

для создания значения std::vector<bool>(num_bools_we_want, false) для ключа 42. Временные векторы не создаются (независимо от оптимизации компилятора).

Единственная «проблема» с этим решением состоит в том, что try_emplace() существует только после C ++ 17. Поскольку вы спрашивали о C ++ 11 - эта версия стандарта представила map::emplace(), что делает почти то же самое, за исключением проблемы с копированием ключа. См. этот вопрос для обсуждения разницы между emplace() и try_emplace().

0 голосов
/ 17 ноября 2018

Обтекание std::vector<bool>

Вы можете обернуть std::vector<bool>, который хотите инициализировать, следующим образом:

template<size_t N>
struct myvector {
   myvector(): data(N) {}
   std::vector<bool> data;
};

Затем объявите mymap как карту, значение которойтип относится к этому типу оболочки myvector<N> вместо std::vector<bool>.Например, для N, равного 100:

std::map<int, myvector<100>> mymap;

Если ключ 42 еще не существует на карте, то:

auto& newvec = mymap[42];

создастэкземпляр типа myvector<100>, который по очереди инициализирует std::vector<bool> размера 100.

Вы можете получить доступ к созданному объекту std::vector<bool> либо через data элемент данных myvector, либо выполнив reinterpret_cast<std::vector<bool>&>(newvec).


Используя std::map::find() и std::map::emplace()

Другой подход заключается в использовании std::map::find() вместо std::map::operator[](), чтобы сначала выяснить, существует ли данный ключ на карте, сравнивая его возвращенный итератор с возвращенным std::map::end().Если данный ключ не существует, то построить вектор, используя std::map::emplace().

В вашем примере newvec можно инициализировать для этого подхода с помощью троичного оператора :

auto it = mymap.find(42); // search for an element with the key 42
bool is_key_in_map = it != mymap.end();
// if the element with the given key exists, then return it, otherwise
// construct it
auto& newvec = is_key_in_map? it->second: 
            mymap.emplace(42, std::vector<bool>(100, true)).first->second;

На самом деле, вы можете напрямую позвонить std::map::emplace() без проверки, существует ли данный ключ, но это будет стоить бесполезного создания временного объекта (т.е. std::vector<bool> объекта), если ключ уже присутствует на карте:

auto& newvec = mymap.emplace(42, std::vector<bool>(100, true)).first->second;

Начиная с C ++ 17: std::map::try_emplace()

Вы можете использовать std::map::try_emplace() вместо std::map::emplace():

auto& newvec = mymap.try_emplace(42, 100, true).first->second;

Таким образом, временный объект, std::vector<bool>(100, true), не будет построено, если карта уже содержит данный ключ (т. Е. Если она уже содержит ключ 42).Следовательно, это более эффективно, чем использование std::map::emplace(), поскольку никакой временный объект не будет создан, если в этом нет необходимости.Тем не менее, он требует C ++ 17.

...