Eficiência do C ++ 11 push_back () com std :: move versus emplace_back () para objetos já construídos

86

Em C ++ 11 emplace_back()geralmente é preferível (em termos de eficiência) push_back(), pois permite a construção no local, mas ainda é o caso ao usar push_back(std::move())com um objeto já construído?

Por exemplo, emplace_back()ainda é preferido em casos como o seguinte?

std::string mystring("hello world");
std::vector<std::string> myvector;

myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)

Além disso, há alguma vantagem no exemplo acima em fazer:

myvector.emplace_back(std::move(mystring));

ou a mudança aqui é totalmente redundante ou não tem efeito?

Tumulto
fonte
myvector.emplace_back(mystring);copia e não se move. Os outros dois movimentos e devem ser equivalentes.
TC
Veja também esta questão e os resultados: Pesquisa solicitada para VC ++ a respeito de inserir e emplace
ildjarn

Respostas:

116

Vamos ver o que as diferentes chamadas fornecidas por você fazem:

  1. emplace_back(mystring): Esta é uma construção no local do novo elemento com qualquer argumento fornecido. Uma vez que você forneceu um lvalue, essa construção no local na verdade é uma construção de cópia, ou seja, isso é o mesmo que chamarpush_back(mystring)

  2. push_back(std::move(mystring)): Isso chama a inserção de movimento, que no caso de std :: string é uma construção de movimento no local.

  3. emplace_back(std::move(mystring)): Novamente, esta é uma construção in-loco com os argumentos que você forneceu. Uma vez que esse argumento é um rvalue, ele chama o construtor de movimento de std::string, ou seja, é uma construção de movimento no local como em 2.

Em outras palavras, se chamados com um argumento do tipo T, seja um rvalue ou lvalue, emplace_backe push_backsão equivalentes.

No entanto, para qualquer outro argumento (s), emplace_backvence a corrida, por exemplo com um char const*em a vector<string>:

  1. emplace_back("foo")requer string::string(char const*)construção no local.

  2. push_back("foo")primeiro tem que chamar string::string(char const*)a conversão implícita necessária para corresponder à assinatura da função e, em seguida, uma inserção de movimento como o caso 2. acima. Portanto, é equivalente apush_back(string("foo"))

Arne Mertz
fonte
1
O construtor de movimento é geralmente mais eficiente do que os construtores de cópia, portanto, usar rvalue (caso 2, 3) é mais eficiente do que usar lvalue (caso 1), apesar da mesma semântica.
Ryan Li
que tal tal situação? void foo (string && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
VALOD9 de
@VALOD esses são os números 1 e 3 da minha lista novamente. spode ser definido como uma referência de rvalue que se liga apenas a rvalues, mas dentro de foo, sé um lvalue.
Arne Mertz
1

O emplace_back obtém uma lista de referências de rvalue e tenta construir um elemento de contêiner direto no local. Você pode chamar emplace_back com todos os tipos que os construtores de elemento de contêiner suportam. Ao chamar emplace_back para parâmetros que não são referências rvalue, ele 'retorna' às referências normais e pelo menos o construtor de cópia é chamado quando o parâmetro e os elementos do contêiner são do mesmo tipo. No seu caso, 'myvector.emplace_back (mystring)' deve fazer uma cópia da string porque o compilador não pode saber que o parâmetro myvector é móvel. Portanto, insira o std :: move o que lhe dá o benefício desejado. O push_back deve funcionar tão bem quanto emplace_back para elementos já construídos.

Tunichtgut
fonte
2
Essa é uma maneira interessante de descrever referências de encaminhamento / universais.
TC