0
votes

I have code like this:

#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() in this case doesn't steal the return variable. Is there a way to avoid the excess copy?

This is the result:

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

new code based on sugestions(dont know if keep asking on same thread dont know if that was what i expected , the reaon i was doind all this move is to reduce code):

#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";
  }
  
}

new results to see it in action

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

1
Get rid of the empty destructor. Writing empty destructors can cause the compiler to generate less-efficient code, as seen herePaulMcKenzie
You dont need to move a temporary. If you want to avoid a copy, try providing your class a move constructor.Taekahn
It occurred to me to mention this, I think you might misunderstand std::move it doesn't move anything. All it does is perform a cast.Taekahn
i thoght it did some moving, most tutorials i read did explained with steal, now i am confuced is what i need move for.user3709120
cppatomic.blogspot.com/2018/02/… should clarify? I'd give scott meyer a read.Taekahn

1 Answers

3
votes

The statement Foo fooBack = std::move(v.back()); does not remove the last Foo object from the vector at all, but it does move the content of that object to your fooBack variable, due to a compiler-generated move constructor for Foo. However, the original Foo object itself is still in the vector, which is why your 2nd for loop still sees that object. The object just may not have a text value anymore to print, because fooBack stole it.

Live Demo

That being said, it is possible that in your case, std::string is utilizing "Short String Optimization" (where small string values are kept in a fixed buffer inside the std::string class itself to avoid allocating memory dynamically when not needed) in such a way that a "move" from one std::string to another simply makes a copy of that fixed buffer's data, leaving the original std::string unchanged. Which would explain why your 2nd for loop is still showing "foo 3" in the vector after "moving" v.back() to the fooBack variable - it was really a copy, not a move.

If you want to actually remove the last Foo object from the vector, you can use v.pop_back() for that, eg:

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

Live Demo

On a side note, your push_back()'s should be using emplace_back() instead, to avoid creating temporary Foo objects that have to be moved from during the pushes:

//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");