No caso particular de um mapa, as opções antigas eram apenas duas: operator[]e insert(sabores diferentes de insert). Então eu vou começar a explicar isso.
O operator[]é um operador de localização ou adição . Ele tentará encontrar um elemento com a chave especificada dentro do mapa e, se existir, retornará uma referência ao valor armazenado. Caso contrário, ele criará um novo elemento inserido no local com a inicialização padrão e retornará uma referência a ele.
A insertfunção (no tipo de elemento único) pega um value_type( std::pair<const Key,Value>), usa a chave ( firstmembro) e tenta inseri-la. Como std::mapnão permite duplicatas, se houver um elemento existente, ele não inserirá nada.
A primeira diferença entre os dois é que operator[]precisa ser capaz de construir um valor inicial padrão e, portanto, é inutilizável para tipos de valor que não podem ser inicializados por padrão. A segunda diferença entre os dois é o que acontece quando já existe um elemento com a chave fornecida. A insertfunção não modifica o estado do mapa, mas retorna um iterador para o elemento (e um falseindicando que não foi inserido).
// assume m is std::map<int,int> already has an element with key 5 and value 0
m[5] = 10; // postcondition: m[5] == 10
m.insert(std::make_pair(5,15)); // m[5] is still 10
No caso do insertargumento, é um objeto de value_type, que pode ser criado de diferentes maneiras. Você pode construí-lo diretamente com o tipo apropriado ou passar qualquer objeto a partir do qual ele value_typepossa ser construído, e é aí que std::make_pairentra em cena, pois permite a criação simples de std::pairobjetos, embora provavelmente não seja o que você deseja ...
O efeito líquido das seguintes chamadas é semelhante :
K t; V u;
std::map<K,V> m; // std::map<K,V>::value_type is std::pair<const K,V>
m.insert( std::pair<const K,V>(t,u) ); // 1
m.insert( std::map<K,V>::value_type(t,u) ); // 2
m.insert( std::make_pair(t,u) ); // 3
Mas não são realmente iguais ... [1] e [2] são realmente equivalentes. Nos dois casos, o código cria um objeto temporário do mesmo tipo ( std::pair<const K,V>) e o passa para a insertfunção. A insertfunção criará o nó apropriado na árvore de pesquisa binária e copiará a value_typeparte do argumento para o nó. A vantagem de usar value_typeé que, bem, value_typesempre corresponde value_type , você não pode digitar errado o tipo dos std::pairargumentos!
A diferença está em [3]. A função std::make_pairé uma função de modelo que criará um std::pair. A assinatura é:
template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );
Não forneci intencionalmente os argumentos do modelo std::make_pair, pois esse é o uso comum. E a implicação é que os argumentos do modelo são deduzidos da chamada, nesse caso, para que seja T==K,U==V, portanto a chamada para std::make_pairretornará a std::pair<K,V>(observe a falta const). A assinatura requer value_typeque seja próximo, mas não o mesmo que o valor retornado da chamada para std::make_pair. Por estar próximo o suficiente, ele criará um temporário do tipo correto e a cópia inicializará. Isso, por sua vez, será copiado para o nó, criando um total de duas cópias.
Isso pode ser corrigido fornecendo os argumentos do modelo:
m.insert( std::make_pair<const K,V>(t,u) ); // 4
Mas isso ainda é propenso a erros da mesma maneira que digitar explicitamente o tipo no caso [1].
Até esse ponto, temos diferentes maneiras de chamar insertque exigem a criação value_typeexterna e a cópia desse objeto no contêiner. Como alternativa, você pode usar operator[]se o tipo é padrão construtível e atribuível (focalizando intencionalmente apenas em m[k]=v) e requer a inicialização padrão de um objeto e a cópia do valor nesse objeto.
Em C ++ 11, com modelos variádicos e encaminhamento perfeito há uma nova maneira de adicionar elementos dentro de um recipiente por meio de emplacing (criando no lugar). As emplacefunções nos diferentes contêineres fazem basicamente a mesma coisa: em vez de obter uma fonte da qual copiar no contêiner, a função usa os parâmetros que serão encaminhados ao construtor do objeto armazenado no contêiner.
m.emplace(t,u); // 5
Em [5], o std::pair<const K, V>não é criado e passado para emplace, mas as referências ao objeto te usão passadas para emplaceque os encaminha ao construtor do value_typesubobjeto dentro da estrutura de dados. Nesse caso, nenhuma cópia std::pair<const K,V>é feita, o que é a vantagem das emplacealternativas C ++ 03. Como no caso insert, não substituirá o valor no mapa.
Uma pergunta interessante sobre a qual eu não havia pensado é como emplacerealmente pode ser implementado para um mapa, e esse não é um problema simples no caso geral.