c++ 如何使用std::move从vector中删除最后一项并更新vector大小

wdebmtf2  于 2023-05-20  发布在  其他
关注(0)|答案(1)|浏览(268)

我有这样的代码:

#include <iostream>
#include <exception>
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
     
struct Foo {
    Foo(std::string t):text(t){}
    //destructor deleted
        
    std::string text;
};

int main()
{
    std::vector<Foo> v;
        
    v.push_back(Foo("foo 1"));
    v.push_back(Foo("foo 2"));
    v.push_back(Foo("foo 3"));
      
    for(auto& foo : v){
        std::cout<<foo.text<<"\n";
    }
      
    Foo fooBack = std::move(v.back());
      
    std::cout<<fooBack.text<<"\n";
      
    for(auto& foo : v){
        std::cout<<foo.text<<"\n";
    }
}

在这种情况下,std::move()不会窃取返回变量。有没有办法避免过度复制?
这就是结果:

foo 1
foo 2
foo 3
foo 3
foo 1
foo 2
foo 3

基于sugestions的新代码(不知道是否继续在同一个线程上问不知道这是否是我所期望的,我做这一切的原因是为了减少代码):

#include <iostream>
#include <exception>
#include <stdexcept>

#include<string>
#include<vector>
#include<utility>
 
struct Foo {
        Foo(std::string t):text(t){
            std::cout<<text<<" construct foo called \n";
        }
        Foo(const Foo& f){
            text = f.text;
            std::cout<<text<<"copy construct foo called \n";
        }
        Foo(Foo&& f){
            text = std::move(f.text);
            std::cout<<text<<"move construct foo called \n";
        
        }
        /*
    ~Foo() {
    }
    */
    
    std::string text;
};
int main()
{
    
    std::vector<Foo> v;
    
  v.emplace_back("foo 1");
  v.emplace_back("foo 2");
  v.emplace_back("foo 3");
  
  for(auto&& foo : v){
    std::cout<<foo.text<<"\n";
  }
  
  Foo fooBack = std::move(v.back());
  v.pop_back();
  
  std::cout<<fooBack.text<<"\n";
  
  for(auto&& foo : v){
    std::cout<<foo.text<<"\n";
  }
  
}

新的结果来看看它的行动

foo 1 construct foo called 
foo 2 construct foo called 
foo 1copy construct foo called 
foo 3 construct foo called 
foo 1copy construct foo called 
foo 2copy construct foo called 
foo 1
foo 2
foo 3
foo 3move construct foo called 
foo 3
foo 1
foo 2
rxztt3cl

rxztt3cl1#

语句Foo fooBack = std::move(v.back());根本不会 * 删除 * vector中的最后一个Foo对象,但它会 * 将该对象的内容 * 移动到fooBack变量中,这是由于编译器为Foo生成了一个移动构造函数。然而,原始的Foo对象本身仍然在向量中,这就是为什么第二个for循环仍然看到该对象的原因。对象可能不再有text值可打印,因为fooBack窃取了它。
Live Demo
也就是说,在您的情况下,std::string可能正在使用“短字符串优化”(其中小字符串值保存在std::string类本身内部的固定缓冲区中,以避免在不需要时动态分配内存),从一个std::string“移动”到另一个std::string只是复制该固定缓冲区的数据,保持原始的std::string不变。这就解释了为什么你的第二个for循环在将v.back()“移动”到fooBack变量后仍然在向量中显示"foo 3"-它实际上是一个副本,而不是移动。
如果你想从向量中删除最后一个Foo对象,你可以使用v.pop_back(),例如:

Foo fooBack = std::move(v.back());
v.pop_back(); // <-- add this

Live Demo
顺便说一句,你的push_back()应该使用emplace_back(),以避免创建临时的Foo对象,这些对象在推送过程中必须被移动:

//v.push_back(Foo("foo 1"));
//v.push_back(Foo("foo 2"));
//v.push_back(Foo("foo 3"));
v.emplace_back("foo 1");
v.emplace_back("foo 2");
v.emplace_back("foo 3");

相关问题