使用std::move有任何性能优势吗?

4
请考虑以下代码:
#include <iostream>
#include <vector>
#include <utility>

std::vector<int> vecTest;
int main() 
{
    int someRval = 3;
    vecTest.push_back(someRval);
    vecTest.push_back(std::move(someRval));
    return 0;
}

据我理解,第一次调用 push_back()时,someRval 的值将被复制到 vecTest 中,但是在第二次调用中,someRval 会生成一个 x 值。我的问题是,是否会有任何性能优势?也许对于 int 来说可能没有,但是当使用更大的对象时,可能会有一些性能优势吗?

2
转换为 rvalue,而不是 lvalue。对于 int 没有任何区别。对于一些类来说会有区别,例如移动构造函数比拷贝构造函数更有效率的 std::string - Igor Tandetnik
3
你提出了一个错误的问题。如果你是在概念上将对象的值移入该向量中(因此不再使用该对象),那么你应该将其移到向量中。正确表达你的意图,性能将是需要的任何内容。 - Nicol Bolas
3个回答

7

移动操作带来的性能优势通常来自于规避了动态分配。

考虑一个过于简化(并且幼稚)的string(缺少复制赋值运算符和移动赋值运算符):

class MyString
{
public:
    MyString() : data(nullptr) {}

    ~MyString()
    {
        delete[] data;
    }

    MyString(const MyString& other) //copy constructor
    { 
        data = new char[strlen(other.c_str()) + 1]; // another allocation
        strcpy(data, other.c_str()); // copy over the old string buffer
    }

    void set(const char* str)
    {
        char* newString = new char[strlen(str) + 1];
        strcpy(newString, str);
        delete[] data;
        data = newString;
    }

    const char* c_str() const
    {
        return data;
    }
private:
    char* data;
};

这很好,但如果您的字符串变得很长,那么复制构造函数可能是昂贵的。然而,复制构造函数需要复制所有内容,因为它不允许触摸other对象,它必须恰如其名地执行复制操作。现在,如果您需要字符串的副本,那么这就是您需要付出的代价,但是如果您只想使用字符串的状态,不关心之后会发生什么,那么您也可以移动它。

移动它只需将other对象保留在某些有效状态,以便我们可以使用other中的所有内容,这正是我们想要的。现在,我们所要做的就是不再复制指向data指针的内容,而是将data指针重新分配给other的指针,我们基本上是窃取other的内容,我们还会将原始的data指针设置为nullptr

MyString(MyString&& other)
{
    data = other.data;
    other.data = nullptr;
}

就这些了。显然,这比复制整个缓冲区要快,就像复制构造函数所做的那样。

示例


我知道这只是一个例子,但为了完整起见,在移动构造函数的开头应该添加一个检查,以确保otherthis不同,如果是这种情况,则不执行任何操作。 - SubMachine

5

移动像int或甚至char*这样的“原始”类型时,与复制它们没有什么不同。

std::string这样的复杂类型可以利用您愿意牺牲源对象状态的信息,使移动比复制更有效率。


0

是的,但这取决于应用程序的细节-对象的大小和操作频率。

将其强制转换为 r-value 并移动它(使用 std:move())可以避免复制。如果对象的大小足够大,则可以节省时间(例如考虑一个包含 1 000 000 个双精度浮点数的数组-复制它通常意味着复制 4 MB 或更多内存)。
另一点是频率-如果您的代码经常执行相应的操作,它可能会累加相当多。

请注意,源对象在此过程中被销毁(变得无法使用),这可能或可能不可接受您的逻辑-您需要理解它并相应编码。如果之后仍需要源对象,则显然行不通。

通常情况下,除非需要优化,否则不要进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接