mercredi 15 janvier 2020

Can I move attributes of temporary objects in C++?

I'm writing an iterator that depends on items in a vector and an iterator-factory that spawns said iterators. The code is conceptually equal to the following:

struct Iter
{
  int i = 0;
  vector<int> vec;

  Iter(const vector<int>& _vec):
    vec(_vec)
  {
    cout << "copied vector of size "<<vec.size()<<" for iterator\n";
  }

  Iter(vector<int>&& _vec):
    vec(move(_vec))
  {
    cout << "moved vector of size "<<vec.size()<<" for iterator\n";
  }

  int& operator*() { return vec[i]; }
  Iter& operator++() { ++i; return *this; }
  bool operator!=(const Iter& _it) const { return false; }
};

struct Factory
{
  vector<int> fac_vec;

  Factory(const vector<int>& _v):
    fac_vec(_v)
  {
    cout << "copied vector of size " << fac_vec.size() << " for factory\n";
  }

  Factory(vector<int>&& _v):
    fac_vec(move(_v))
  {
    cout << "moved vector of size "<<fac_vec.size()<<" for factory\n";
  }

  Iter begin() { return Iter(fac_vec); }
  Iter end() { return Iter({}); }
};
int main(){
  for(const int i: Factory({1,2,3}))
    cout << i << "\n";
  return 0;
}

Now, running this code gives me (g++ 8.3):

moved vector of size 3 for factory   [ initialization of the factory with {1,2,3} - moved ]
moved vector of size 0 for iterator  [ initialization of the end-iterator with {} - moved ]
copied vector of size 3 for iterator [ initialization of the begin-iterator with fac_vec - copied ]

This is kind of disappointing as I was hoping that the last begin() would call Iter's move-constructor since the factory is being destroyed right after (isn't it?) and the compiler has all the information it needs to decide this.

I figure I could do what I want using std::shared_ptr but this incurs overhead both in the code and in the program. I'd much rather tell the compiler to move fac_vec when the last begin() is called. Is there a way to do this?

Aucun commentaire:

Enregistrer un commentaire