Já que C ++ 17 std::map
oferece dois novos métodos de inserção: insert_or_assign()
e try_emplace()
, como também mencionado no comentário de sp2danny .
insert_or_assign()
Basicamente, insert_or_assign()
é uma versão "aprimorada" do operator[]
. Em contraste com operator[]
, insert_or_assign()
não exige que o tipo de valor do mapa seja construtível por padrão. Por exemplo, o código a seguir não é compilado, porque MyClass
não tem um construtor padrão:
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;
}
No entanto, se você substituir myMap[0] = MyClass(1);
pela linha a seguir, o código será compilado e a inserção ocorrerá conforme o esperado:
myMap.insert_or_assign(0, MyClass(1));
Além disso, semelhante a insert()
, insert_or_assign()
retorna a pair<iterator, bool>
. O valor booleano é true
se uma inserção ocorreu e false
se uma atribuição foi feita. O iterador aponta para o elemento que foi inserido ou atualizado.
try_emplace()
Semelhante ao anterior, try_emplace()
é uma "melhoria" de emplace()
. Em contraste com emplace()
, try_emplace()
não modifica seus argumentos se a inserção falhar devido a uma chave já existente no mapa. Por exemplo, o código a seguir tenta substituir um elemento por uma chave que já está armazenada no mapa (consulte *):
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;
}
Saída (pelo menos para VS2017 e Coliru):
pMyObj não foi inserido
pMyObj foi modificado de qualquer maneira
Como você pode ver, pMyObj
não aponta mais para o objeto original. No entanto, se você substituir auto [it, b] = myMap2.emplace(0, std::move(pMyObj));
pelo código a seguir, a saída parecerá diferente, porque pMyObj
permanece inalterada:
auto [it, b] = myMap2.try_emplace(0, std::move(pMyObj));
Resultado:
pMyObj não foi inserido
pMyObj pMyObj.m_i = 2
Código em Coliru
Observação: tentei manter minhas explicações o mais curtas e simples possível para encaixá-las nessa resposta. Para uma descrição mais precisa e abrangente, recomendo a leitura deste artigo sobre Fluent C ++ .