std::vector::emplace_back 和 std::move

std::vector::emplace_back and std::move

std::vector::emplace_backstd::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_backstd::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*);

所以没有临时工。

第三个例子是废话。你不能移动文字。