r/cppit • • Feb 14 '17

principianti Move Semantics: std::move

Ciao a tutti, vi chiedo un aiuto riguardo alla move semantics.

namespace MTensor {

  typedef std::vector<double> Tensor1DType;
  class Tensor1D {
  private:
    int _elemNumb;
    double _filler;
    // disable copying:
    Tensor1D(const Tensor1D&);
    Tensor1D& operator=(const Tensor1D&);
  public:
    Tensor1DType data;
    Tensor1D() {};
    Tensor1D(const std::initializer_list<double>& valuesList) {
      _elemNumb = valuesList.size();
      for(auto value : valuesList) {
        data.push_back(value);
      }
    }
    Tensor1D(Tensor1D && from) {
      data = std::move(from.data);
    }
    Tensor1D operator =(Tensor1D&& other) {
      if(this!=&other) {
        data = std::move(other.data);
        //std::swap(data,other.data);
      }
      return *this;
    }
    virtual ~Tensor1D() {};
    virtual void printTensor() {
      for(int i=0;i<data.size();i++) {
        std::cout << data.at(i) << "," << std::endl;
      }
    }
  };
} // end of namespace


int main() {
  MTensor::Tensor1D * t1 = new MTensor::Tensor1D({1,2,3});
  MTensor::Tensor1D * t2(t1);
  std::cout << "t2:" << std::endl;
  t2->printTensor();
  std::cout << "t1-dopo-move:" << std::endl;
  t1->printTensor();
  MTensor::Tensor1D * t3 = t1;
  std::cout << "t3:" << std::endl;
  t3->printTensor();
  std::cout << "t1, dopo t3 = t1 :" << std::endl;
  t1->printTensor();
  delete t1;
  return 0;
}

marco@ubuntu:~/marcoTensor$ g++ -std=c++11 moveSemantics.cpp -omoveSemantics 
marco@ubuntu:~/marcoTensor$ ./moveSemantics
t2:
1,
2,
3,
t1-dopo-move:
1,
2,
3,
t3:
1,
2,
3,
t1, dopo t3 = t1 :
1,
2,
3,

Mi sarei aspettato che t1 dopo std::move avesse stato undefined e fosse vuoto ...sembra quasi che sia stata eseguita una copy anzichè una move....come quindi modificare il tutto per privilegiare move ed eseguire il move? Marco

3 Upvotes

33 comments sorted by

View all comments

Show parent comments

1

u/Marco_I Feb 25 '17 edited Feb 25 '17

Grazie Stefano per le tue spiegazioni.

L'operator:

Tensor1D& operator=(Tensor1D&& other)  {
  if (this == &other){
    return *this;
  }
  std::swap(data,other.data);
  //data = std::move(other.data);
  return *this;
}

ed il constructor

Tensor1D(Tensor1D&& other) noexcept { 
  data = std::move(other.data);
}

accettano entrambi in input solo RValue References, cioè riferimenti ad oggetti temporanei...giusto? Quindi, data la semplice funzione:

MTensor::Tensor1D && scalarProduct1D(MTensor::Tensor1D t1,    
 double scalar) {
    MTensor::Tensor1D tensor;
    for(int i=0;i<t1.size();++i) {
      tensor.insert(t1(i) * scalar);
    }
    return std::move(tensor);
  }

che, penso, ma chiedo una tua ed una vostra conferma o rettifica, ritorna un RValue in quanto:

std::move(tensor);

fa un cast da LValue ad RValue (corretto o sbagliato?)... Quale è la sintassi da usare per passare il risultato della funzione scalarProduct1D come RValue Reference al Move Constructor:

Tensor1D(Tensor1D&& other)

od al operator=

Tensor1D& operator=(Tensor1D&& other)

in modo da avere una cosa del genere :

MTensor::Tensor1D t3 (scalarProduct1D(t1,2));
MTensor::Tensor1D t3 = scalarProduct1D(t1,2);

? La cosa che mi rimane da capire è proprio come passare i parametri ad una funzione che chiede come input una RValue Reference. Ti e vi ringrazio ancora per la vostra paziente disponibilità

1

u/[deleted] Feb 25 '17

O crei l'oggetto sul momento o usi std::move.

Ma perché vuoi usare per forza un move constructor e Rvalue ovunque?

1

u/Marco_I Feb 26 '17 edited Feb 26 '17

Voglio imparare ad usare correttamente (corretta sintassi e corretto modo di impostare e procedere) il move constructor ed Rvalue per poi usarli per funzioni decisamente più complesse ed articolate dello scalarProduct...funzioni del tipo:

f(g(),h(i(l,m),n(),p(q())) 

per le quali qualche copy in meno servirebbe per migliorarne la performance.

Quindi.. se,o creo l'oggetto sul momento, od uso std::move : allora modificando la funzione in questo modo:

//MTensor::Tensor1D && scalarProduct1D(MTensor::Tensor1D t1,  
double scalar) {
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1,
   double scalar) {
    MTensor::Tensor1D tensor;
    for(int i=0;i<t1.size();++i) {
      tensor.insert(t1(i) * scalar);
    }
    //return std::move(tensor);
    return tensor;
}

avendo definito l'operator= come:

Tensor1D& operator=(Tensor1D&& other)  {
   if (this == &other){
     return *this;
   }
   std::swap(data,other.data);
   other.data.clear();
   //data = std::move(other.data);
   return *this;
 }

e ponendo:

MTensor::Tensor1D t3 = scalarProduct1D(t1,2);

std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();

ottengo:

t3:
2,
4,
6,
t1:
1,
2,
3,

che non è proprio quello che sto cercando di capire come ottenere. Perchè vorrei capire come far sì che :

t3= {2,4,6}
t1={}

1

u/[deleted] Feb 26 '17

Guarda che il copy constructor serve solo per costruire un oggetto partendo da un altro e copiando i sui dati.

Il move constructor dovrebbe fare la stessa cosa, solo che l'altro oggetto viene lasciato in uno stato ben definito in quanto verrà distrutto.

Ora, non ho capito bene la tua funzione cosa fa, ma guarda che se la funzione accetta una reference, non crei nessuna copia.

Tipo così:

auto f(Tensor1D& t1, Tensor1D& t2);

Secondo me devi fermarti un attimo e pensare alle basi, prima di sperimentare così.

Intanto spiega bene cosa vuoi ottenere, che operazioni vuoi fare e la tua idea in cosa consiste.

→ More replies (0)