std::vector 和移动语义

Posted

技术标签:

【中文标题】std::vector 和移动语义【英文标题】:std::vector and move semantics 【发布时间】:2021-12-12 15:04:56 【问题描述】:

要在std::vector 上启用移动语义,我需要按值传递吗?所以编译器不会复制元素而只是移动它们?

例子:

class data

public:
    void setMyData(vector<string> sourceData)
    
        private_data_ = sourceData;
    

private:
    vector<string> private_data_;
;

【问题讨论】:

这能回答你的问题吗? What is move semantics? 你可能只需要void setMyData(const vector&lt;string&gt; &amp; sourceData)。这里sourceData 是对您传递的向量的常量引用。不涉及复制。 setMyData 对向量做了什么? Mabe,这是一个XY Problem,请确保您需要告诉我们更多信息。 @Jabberwocky 纠正我,如果我错了,但是如果你通过 const 引用传递,如果我想要将内存从一个对象移动到另一个对象,那么 sourceData 怎么可能是一个空向量,因为我不想要复制矢量但只是移动它? 我想我得到了你想要的。请确认: : 当你打电话给somedata.SetMyData(somevector); 之后somevector 应该是空的吗? 【参考方案1】:

在 C++ stl 向量上启用移动语义

您对移动语义概念有误解。

std::vector itself is move constructable 定义了移动构造函数。您无需显式启用它,而是正确使用它。

在函数中

void setMyData(vector<string> sourceData)  // copy 1

   private_data_= sourceData; // copy 2

你正在做双重复制。你需要

void setMyData(vector<string> sourceData)  // copy 

   private_data_= std::move(sourceData); // move

或者你可能想要

void setMyData(vector<string>&& sourceData)  // only accept the rvalue

   private_data_= std::move(sourceData); // move

对于第二种情况,你调用函数

setMyData(std::move(sourceData));

【讨论】:

嗨,“启用”我是想利用它,可能我没有正确表达自己。 我要移动不复制原始向量可以为空之后这不是问题我拿起了 void setMyData(vector sourceData)private_data_=std::move(pirceData); @user1583007 “原始向量可以是空的,这不是问题”:如果是这样,您也可以使用第二个,您可以确保调用者只能移动构造或将右值传递给函数。 setMyData(std::move(sourceData)); 也可以用于第一个版本。 @Superlokkus 我想你没有阅读他上面的评论。我没有假设任何事情:“原始向量可以为空之后不是问题”:-> OP @Superlokkus setMyData(foo) 清空foo 向量不一定是坏习惯,这取决于用例。如果之后实际上不再需要 foo 向量,这是最有效的解决方案,因为绝对不涉及复制。【参考方案2】:

我建议使用该函数的 2 重载,甚至使用通用的:

#include <vector>
#include <string>

class data

public:
    void setMyData(const std::vector<std::string>& sourceData)private_data_=sourceData;

    void setMyData(std::vector<std::string>&& sourceData)private_data_= std::move(sourceData);

    template <typename T>
    void genericsetMyData(T&& source) private_data_ = std::forward<T>(source);

    private:
    std::vector<std::string> private_data_;
;

int main() 
    class data a,b,c,d;
    std::vector<std::string> s1,s2; const std::vector<std::string> s3;
    a.setMyData(s1);
    b.setMyData(std::move(s2));
    c.genericsetMyData(s1);
    d.genericsetMyData((std::move(s1)));
    d.genericsetMyData(s3);

模板化的比较复杂,因为它使用所谓的转发引用。

【讨论】:

你确定吗?对于转发引用,我经常收到关于 std::forward 与 std::move 的相互矛盾的建议。【参考方案3】:

我建议如下:

#include <utility>

class data

public:
    void setMyData(vector<string> sourceData)
    
          private_data_ = std::move(sourceData);
    
        
private:
    vector<string> private_data_;
;

而要初始化,您有两种方法:

vector<string> myStrings"hello", "i am", "stringies";
data mydata;

// Type 1: Give ownership
mydata.setMyData(std::move(myStrings)); // not a single copy, but myStrings is empty

// Type 2: Copy
mydata.setMyData(myStrings); // Copy once only, and myStrings is still valid

这种技术的好处是您不必编写多个重载方法,您可以选择要传递参数的方式。

【讨论】:

以上是关于std::vector 和移动语义的主要内容,如果未能解决你的问题,请参考以下文章

移动语义的一切

在 return 语句中移动构造函数中的语义

首先创建字符串然后通过移动语义将其添加到向量或在向量中创建元素是不是具有内存效率?

如果分配器提供 realloc 语义,std::vector 可以避免复制吗?

c++ 移动语义及使用(续)

“语义”是啥意思?为啥“移动语义”这样命名,而不是任何其他术语?