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"));
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.
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.
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));
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.