web-dev-qa-db-fra.com

std :: vector :: emplace_back et std :: move

Y a-t-il un avantage à utiliser std::vector::emplace_back et std::move ensemble? ou c'est simplement redondant puisque std::vector::emplace_back fera une construction sur place?

Cas à clarifier:

std::vector<std::string> bar;

Premier:

bar.emplace_back(std::move(std::string("some_string")));

Deuxième:

std::string str("some_string");
bar.emplace_back(std::move(str));

Troisième:

bar.emplace_back(std::move("some_string"));
24
Humam Helfawi

Dans la deuxième version, il y a un avantage. L'appel de emplace_back Appellera le constructeur de déplacement de std::string Lorsque std::move Est utilisé, ce qui pourrait économiser sur une copie (tant que cette chaîne n'est pas stockée dans un tampon SSO) . Notez que c'est essentiellement la même chose que Push_back Dans ce cas.

std::move Dans la première version n'est pas nécessaire, car la chaîne est déjà une valeur.

std::move Dans la troisième version n'est pas pertinent, car un littéral de chaîne ne peut pas être déplacé.

La méthode la plus simple et la plus efficace est la suivante:

bar.emplace_back("some_string");

Cela ne nécessite aucune construction std::string Inutile car le littéral est parfaitement transmis au constructeur.

25
TartanLlama

emplace_back appelle quelque chose comme

new (data+size) T(std::forward<Args>(args)...);

si args sont basiques - non référencés par rvalue std::string, l'expression sera compilée en

new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)

ce qui signifie que le constructeur de copie aura lieu. mais si vous utilisez std::move sur str, le code sera compilé en

new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)

donc déplacer la sémantique a lieu. c'est un énorme gain de performances.
notez que str est lvalue, il a un nom, donc afin de créer une r-value-reference, vous devez utiliser std::move.

dans l'exemple

vec.emplace_back("some literal"); 

le code se compilera pour

new (data+size) std::string("literal"); //calls std::string::string(const char*);

donc pas de temporaires.

le troisième exemple est un non-sens. vous ne pouvez pas déplacer des littéraux.

6
David Haim

L'idée de emplace_back est de se débarrasser des opérations de copie et de déplacement. Vous avez juste besoin de passer les paramètres d'entrée de std::string en emplace_back. UNE std::string l'objet sera construit à l'intérieur de emplace_back méthode.

bar.emplace_back("some_string");

Si vous avez déjà une chaîne, il est judicieux d'utiliser std::move. UNE std::string l'objet sera construit à l'intérieur de emplace_back en déplaçant les données de str.

std::string str("some_string");
bar.emplace_back(std::move(str));
4
Stas

Il est utile de le faire dans le deuxième cas. Considérez ce code:

int main()
{
    std::vector<std::string> bar;
    std::string str("some_string");
    bar.emplace_back(std::move(str)); str.clear();
    // bar.emplace_back(str);
    std::cout << str << std::endl;
}

Si vous modifiez le commentaire à la ligne ci-dessus, vous pouvez voir que vous vous retrouverez avec deux copies de "some_string" (une dans bar et une dans str). Cela change donc quelque chose.

Sinon, le premier déplace un temporaire, et le troisième déplace un littéral de chaîne constante. Ça ne fait rien.

1
Ami Tavory