Поскольку C ++ 17 std::map
предлагает два новых метода вставки: insert_or_assign()
и try_emplace()
, как также упоминалось в комментарии sp2danny .
insert_or_assign()
По сути, insert_or_assign()
это «улучшенная» версия operator[]
. В отличие от operator[]
, insert_or_assign()
не требует, чтобы тип значения карты был конструктивным по умолчанию. Например, следующий код не компилируется, потому MyClass
что не имеет конструктора по умолчанию:
class MyClass {
public:
MyClass(int i) : m_i(i) {};
int m_i;
};
int main() {
std::map<int, MyClass> myMap;
// VS2017: "C2512: 'MyClass::MyClass' : no appropriate default constructor available"
// Coliru: "error: no matching function for call to 'MyClass::MyClass()"
myMap[0] = MyClass(1);
return 0;
}
Однако, если вы замените myMap[0] = MyClass(1);
следующую строку, тогда код компилируется и вставка происходит, как задумано:
myMap.insert_or_assign(0, MyClass(1));
Более того, как и insert()
, insert_or_assign()
возвращает pair<iterator, bool>
. Логическое значение - true
если произошла вставка и было false
ли выполнено присвоение. Итератор указывает на элемент, который был вставлен или обновлен.
try_emplace()
Подобно вышесказанному, try_emplace()
это «улучшение» emplace()
. В отличие от emplace()
, try_emplace()
не изменяет свои аргументы, если вставка не удалась из-за ключа, уже существующего на карте. Например, следующий код пытается разместить элемент с ключом, который уже хранится на карте (см. *):
int main() {
std::map<int, std::unique_ptr<MyClass>> myMap2;
myMap2.emplace(0, std::make_unique<MyClass>(1));
auto pMyObj = std::make_unique<MyClass>(2);
auto [it, b] = myMap2.emplace(0, std::move(pMyObj)); // *
if (!b)
std::cout << "pMyObj was not inserted" << std::endl;
if (pMyObj == nullptr)
std::cout << "pMyObj was modified anyway" << std::endl;
else
std::cout << "pMyObj.m_i = " << pMyObj->m_i << std::endl;
return 0;
}
Вывод (по крайней мере, для VS2017 и Coliru):
pMyObj не был вставлен
pMyObj все равно был изменен
Как видите, pMyObj
больше не указывает на исходный объект. Однако, если вы замените auto [it, b] = myMap2.emplace(0, std::move(pMyObj));
следующий код, результат будет выглядеть иначе, потому что pMyObj
останется неизменным:
auto [it, b] = myMap2.try_emplace(0, std::move(pMyObj));
Вывод:
pMyObj не был вставлен
pMyObj pMyObj.m_i = 2
Код на Колиру
Обратите внимание: я старался, чтобы мои объяснения были как можно короче и просты, чтобы уместить их в этот ответ. Для более точного и исчерпывающего описания я рекомендую прочитать эту статью о Fluent C ++ .