std::vector::emplace_back 和 std::move
std::vector::emplace_back and std::move
std::vector::emplace_back
和std::move
一起使用有什么好处吗?或者它只是多余的,因为 std::vector::emplace_back
会进行就地构造?
澄清案例:
std::vector<std::string> bar;
第一个:
bar.emplace_back(std::move(std::string("some_string")));
第二个:
std::string str("some_string");
bar.emplace_back(std::move(str));
第三个:
bar.emplace_back(std::move("some_string"));
在第二个版本中,有一个优势。调用 emplace_back
将在使用 std::move
时调用 std::string
的移动构造函数,这可以保存一个副本(只要该字符串未存储在 SSO 缓冲区中)。请注意,这与本例中的 push_back
基本相同。
std::move
在第一个版本中是不必要的,因为字符串已经是纯右值。
std::move
在第三个版本中是无关紧要的,因为不能从中移动字符串文字。
最简单高效的方法是这样的:
bar.emplace_back("some_string");
不需要不必要的 std::string
构造,因为文字已完美转发给构造函数。
在第二种情况下这样做是有道理的。考虑这段代码:
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;
}
如果将注释更改为上面的行,您会看到最终会得到两份 "some_string"(一份在 bar
中,一份在 str
中)。所以它确实改变了一些东西。
否则,第一个是移动一个临时的,第三个是移动一个常量字符串文字。它什么都不做。
emplace_back
的整个想法是摆脱复制和移动操作。您只需要将 std::string
的输入参数传递给 emplace_back
。 std::string
对象将在 emplace_back
方法中构造。
bar.emplace_back("some_string");
如果您已经有一个字符串,那么使用 std::move
是有意义的。通过从 str
.
移动数据,将在 emplace_back
内构造一个 std::string
对象
std::string str("some_string");
bar.emplace_back(std::move(str));
emplace_back
调用
new (data+size) T(std::forward<Args>(args)...);
如果 args
是基本的 - 非右值引用 std::string
,表达式将编译为
new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)
意味着复制构造函数将发生。
但是,如果您在 str
上使用 std::move
,代码将编译为
new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)
因此发生了移动语义。这是一个巨大的性能提升。
请注意,str
是左值,它有一个名称,因此为了从中创建右值引用,您必须使用 std::move
.
示例中
vec.emplace_back("some literal");
代码将编译为
new (data+size) std::string("literal"); //calls std::string::string(const char*);
所以没有临时工。
第三个例子是废话。你不能移动文字。
std::vector::emplace_back
和std::move
一起使用有什么好处吗?或者它只是多余的,因为 std::vector::emplace_back
会进行就地构造?
澄清案例:
std::vector<std::string> bar;
第一个:
bar.emplace_back(std::move(std::string("some_string")));
第二个:
std::string str("some_string");
bar.emplace_back(std::move(str));
第三个:
bar.emplace_back(std::move("some_string"));
在第二个版本中,有一个优势。调用 emplace_back
将在使用 std::move
时调用 std::string
的移动构造函数,这可以保存一个副本(只要该字符串未存储在 SSO 缓冲区中)。请注意,这与本例中的 push_back
基本相同。
std::move
在第一个版本中是不必要的,因为字符串已经是纯右值。
std::move
在第三个版本中是无关紧要的,因为不能从中移动字符串文字。
最简单高效的方法是这样的:
bar.emplace_back("some_string");
不需要不必要的 std::string
构造,因为文字已完美转发给构造函数。
在第二种情况下这样做是有道理的。考虑这段代码:
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;
}
如果将注释更改为上面的行,您会看到最终会得到两份 "some_string"(一份在 bar
中,一份在 str
中)。所以它确实改变了一些东西。
否则,第一个是移动一个临时的,第三个是移动一个常量字符串文字。它什么都不做。
emplace_back
的整个想法是摆脱复制和移动操作。您只需要将 std::string
的输入参数传递给 emplace_back
。 std::string
对象将在 emplace_back
方法中构造。
bar.emplace_back("some_string");
如果您已经有一个字符串,那么使用 std::move
是有意义的。通过从 str
.
emplace_back
内构造一个 std::string
对象
std::string str("some_string");
bar.emplace_back(std::move(str));
emplace_back
调用
new (data+size) T(std::forward<Args>(args)...);
如果 args
是基本的 - 非右值引用 std::string
,表达式将编译为
new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)
意味着复制构造函数将发生。
但是,如果您在 str
上使用 std::move
,代码将编译为
new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)
因此发生了移动语义。这是一个巨大的性能提升。
请注意,str
是左值,它有一个名称,因此为了从中创建右值引用,您必须使用 std::move
.
示例中
vec.emplace_back("some literal");
代码将编译为
new (data+size) std::string("literal"); //calls std::string::string(const char*);
所以没有临时工。
第三个例子是废话。你不能移动文字。