Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 14
  1. #1

    [C++] Vettori matematici

    Ciao a tutti. Sto cercando di creare una classe che rappresenta dei vettori matematici.

    Volevo chiedere agli esperti se utilizzo la move semantics in modo corretto e in caso contrario dove sbaglio e perché. Inoltre ottengo i warning seguenti per gli operatori friend della classe e non riesco proprio a capire perché.
    codice:
    In file included from testVector.cc:1:0:
    Vector.h:50:51: warning: friend declaration ‘Vector<T> operator*(T, const Vector<T>&)’ declares a non-template function [-Wnon-template-friend]
    Vector.h:50:51: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
    Vector.h:51:51: warning: friend declaration ‘Vector<T> operator*(const Vector<T>&, T)’ declares a non-template function [-Wnon-template-friend]
    Vector.h:52:68: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const Vector<T>&)’ declares a non-template function [-Wnon-template-friend]
    g++gcc-4.6.x     testVector.o   -o testVector
    Questo é il mio codice:
    codice:
    #ifndef VECTOR_H
    #define VECTOR_H
    
    #include <vector>
    // std::vector
    #include <initializer_list>
    // std::initializer_list
    #include <iostream>
    // std::ostream
    #include <string>
    // std::string
    #include <stdexcept>
    // std::range_error
    #include <algorithm>
    // std::copy
    // std::equal
    #include <iterator>
    // std::ostream_iterator
    #include <utility>
    // std::move
    
    template<typename T = double>
    class Vector
    {
    public:
    	// --- CONSTRUCTORS ---
    	Vector(size_t);
    	Vector(T, T, T);
    	Vector(const std::vector<T> &);
    	Vector(const std::initializer_list<T>&);
    	Vector(const Vector<T>&);
    	Vector(Vector<T>&&);
    	
    	// --- DESTRUCTOR ---
    	virtual ~Vector();
    	
    	// --- INNER OPERATORS ---
    	Vector<T>& operator=(const Vector<T>&);
    	Vector<T>& operator=(const Vector<T>&&);
    	bool operator==(const Vector<T>&) const;
        bool operator!=(const Vector<T>&) const;
        Vector<T>& operator+=(const Vector<T>&);
        Vector<T>& operator-=(const Vector<T>&);
        Vector<T> operator+(const Vector<T>&) const;
        Vector<T> operator-(const Vector<T>&) const;
        Vector<T> operator-() const;
        T operator*(const Vector<T>&) const;
        Vector<T> operator^(const Vector<T>&) const;
        T operator[](size_t) const;
        friend Vector<T> operator*(T, const Vector<T>&);
        friend Vector<T> operator*(const Vector<T>&, T);
        friend std::ostream& operator<<(std::ostream&, const Vector<T>&);
        
        // --- METHODS ---
        size_t size() const;
        double norm() const;
    	
    protected:
    	// --- ATTRIBUTES ---
    	std::vector<T> v;
    
    	// --- METHODS ---
    	void range_except(const Vector<T>&, const std::string&) const;
    }; 
    
    #endif
    
    // *** PUBLIC ***
    // --- CONSTRUCTORS ---
    
    // Empty vector
    template<typename T>
    Vector<T>::Vector(size_t size)
    : v(size, 0)
    {}
    
    // 3D vector
    template<typename T>
    Vector<T>::Vector(T x, T y, T z)
    : v({x, y, z})
    {}
    
    template<typename T>
    Vector<T>::Vector(const std::vector<T> & V)
    : v(V)
    {}
    
    template<typename T>
    Vector<T>::Vector(const std::initializer_list<T>& V)
    :  v(V)
    {}
    
    template<typename T>
    Vector<T>::Vector(const Vector<T>& V)
    : v(V)
    {}
    
    template<typename T>
    Vector<T>::Vector(Vector<T>&& V)
    : v(std::move(V))
    {}
    
    // ---DESTRUCTOR ---
    template<typename T>
    Vector<T>::~Vector()
    {}
    
    // --- INNER OPERATORS ---
    template<typename T>
    Vector<T>& Vector<T>::operator=(const Vector<T>& V)
    {
    	if(&V != this)
    	{
    		v = V;
    	}
    	
    	return *this;
    }
    
    template<typename T>
    Vector<T>& Vector<T>::operator=(const Vector<T>&& V)
    {
    	if(&V != this)
    	{
    		v = std::move(V);
    	}
    	
    	return *this;
    }
    
    
    template<typename T>
    bool Vector<T>::operator==(const Vector<T>& V) const
    {
    	if(v.size() == V.size())
    	{
    		return equal(v.cbegin(), v.cend(), V.begin());
    	}
    	
    	return false;
    }
    
    template<typename T>
    bool Vector<T>::operator!=(const Vector<T>& V) const
    {
    	return !( (*this) == V);
    }
    
    template<typename T>
    Vector<T>& Vector<T>::operator+=(const Vector<T>& V)
    {
    	range_except(V, "");
    	
    	for(int i(0); i < v.size(); i++)
    	{
    		v[i] += V.v[i];
    	}
    	
    	return *this;
    }
    
    template<typename T>
    Vector<T>& Vector<T>::operator-=(const Vector<T>& V)
    {
    	range_except(V, "");
    	
    	for(int i(0); i < v.size(); i++)
    	{
    		v[i] -= V.v[i];
    	}
    	
    	return *this;
    }
    
    template<typename T>
    Vector<T> Vector<T>::operator+(const Vector<T>& V) const
    {
    	range_except(V, "");
    	
    	Vector<T> r(*this);
    	
    	for(int i(0); i < v.size(); i++)
    	{
    		r.v[i] += V.v[i];
    	}
    	
    	return std::move(r);
    }
    
    template<typename T>
    Vector<T> Vector<T>::operator-(const Vector<T>& V) const
    {
    	range_except(V, "");
    	
    	Vector<T> r(*this);
    	
    	for(auto i : v)
    	{
    		r.v[i] -= V.v[i];
    	}
    	
    	return std::move(r);
    }
    
    template<typename T>
    Vector<T> Vector<T>::operator-() const
    {
    	Vector<T> r(*this);
    	
    	 for (size_t i(0); i < v.size(); i++) 
        {
            r.v[i] = (-1) * r.v[i];
        }
        
        return std::move(r);
    }
    
    template<typename T>
    T Vector<T>::operator*(const Vector<T>& V) const
    {
    	range_except(V, "");
    
    	T r(0);
    	
    	for (size_t i(0); i < v.size(); i++) 
        {
            r += v[i] * V.v[i];
        }
        
        return r;
    }
    
    template<typename T>
    Vector<T> Vector<T>::operator^(const Vector<T>& V) const
    {
    	range_except(V, "");
    	
    	if (v.size() != 3)
        {
            throw std::range_error("Cross product works only for 3D vectors");
        }
        
        Vector<T> r(v[1] * V.v[2] - v[2] * V.v[1],
                    v[2] * V.v[0] - v[0] * V.v[2],
                    v[0] * V.v[1] - v[1] * V.v[0]);
        
        return std::move(r);
    }
    
    template<typename T>
    T Vector<T>::operator[](size_t i) const
    {
    	return v[i];
    }
    
    template<typename T>
    Vector<T> operator*(T x, const Vector<T>& V)
    {
    	Vector<T> r(V);
        
        for (size_t i(0); i < r.v.size(); i++) 
        {
            r.v[i] *= x;
        }
        
        return std::move(r);
    }
    
    template<typename T>
    Vector<T> operator*(const Vector<T>& V, T x)
    {
    	Vector<T> r(V);
        
        for (size_t i(0); i < r.v.size(); i++) 
        {
            r.v[i] *= x;
        }
        
        return std::move(r);
    }
      
    template<typename T>  
    std::ostream& operator<<(std::ostream& out, const Vector<T>& V)
    {
    	std::copy(V.v.cbegin(), V.v.cend(), std::ostream_iterator<T>(out, " "));
    	
    	return out;
    }
    
    // --- METHODS ---
    
    template<typename T>
    size_t Vector<T>::size() const
    {
    	return v.size();
    }
    
    template<typename T>
    double Vector<T>::norm() const
    {
    	return std::sqrt((*this) * (*this));
    }
    
    // *** PRIVATE ***
    
    // --- METHODS ---
    template<typename T>
    void Vector<T>::range_except(const Vector<T>& V, const std::string& s) const
    {
    	if(v.size() != V.size())
    	{
    		throw std::range_error(s);
    	}
    }
    Grazie,
    R.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    codice:
    Vector<T>& operator=(const Vector<T>&& );
    Togli il const, perché il parametro deve essere modificabile.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Originariamente inviato da shodan
    codice:
    Vector<T>& operator=(const Vector<T>&& );
    Togli il const, perché il parametro deve essere modificabile.
    In pratica é l'argomento stesso che viene spostato?

    Se scrivo
    codice:
    v1 = v2;
    é come se invocassi la funzione
    codice:
    v1.operator=(v2);
    e nel caso della move semantics v2 viene letteralmente spostato in v1 e quindi modificato? È per questo che devo togliere il const?

    Grazie.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  4. #4
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Originariamente inviato da RooccoXXI
    In pratica é l'argomento stesso che viene spostato?
    E' il contenuto dell'argomento a essere spostato, ma poi quel contenuto è "annullato". Nel caso di un vector è possibile che venga effettuato un .resize(0);

    Se scrivo
    codice:
    v1 = v2;
    é come se invocassi la funzione
    codice:
    v1.operator=(v2);
    e nel caso della move semantics v2 viene letteralmente spostato in v1 e quindi modificato? È per questo che devo togliere il const?
    Grazie.
    Così però non invochi la move semantics ma la copy semantics.
    Corretto è:
    codice:
    v1 = std::move(v2);
    v1.operator=(std::move(v2));
    Se non espliciti l'operazione saranno invocati il copy constructor e l'assignment operator.
    Le funzioni corrette sono:
    codice:
    template<typename T>
    Vector<T>::Vector(Vector<T>&& V)
    : v(std::move(V.v))
    {
    // opzionale perché ci pensa già il vector
       V.v.resize(0);
    }
    
    template<typename T>
    Vector<T>& Vector<T>::operator=(Vector<T>&& V)
    {
    	if(&V != this)
    	{
    		v = std::move(V.v);
                   // opzionale perché ci pensa già il vector
                   V.v.resize(0);
    	}
    	return *this;
    }
    //////////////////////////////////////////////////////////////////////////////////////
    codice:
    template<typename T>
    Vector<T> Vector<T>::operator-(const Vector<T>& V) const
    {
    	range_except(V, "");
    	
    	Vector<T> r(*this);
    	
    	for(auto i : v)               // ???
    	{
    		r.v[i] -= V.v[i];
    	}
    	
    	return std::move(r);
    }
    Errare è umano, perseverare è diabolico.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  5. #5
    Originariamente inviato da shodan
    Le funzioni corrette sono:
    codice:
    template<typename T>
    Vector<T>::Vector(Vector<T>&& V)
    : v(std::move(V.v))
    {
    // opzionale perché ci pensa già il vector
       V.v.resize(0);
    }
    
    template<typename T>
    Vector<T>& Vector<T>::operator=(Vector<T>&& V)
    {
    	if(&V != this)
    	{
    		v = std::move(V.v);
                   // opzionale perché ci pensa già il vector
                   V.v.resize(0);
    	}
    	return *this;
    }
    //////////////////////////////////////////////////////////////////////////////////////
    codice:
    template<typename T>
    Vector<T> Vector<T>::operator-(const Vector<T>& V) const
    {
    	range_except(V, "");
    	
    	Vector<T> r(*this);
    	
    	for(auto i : v)               // ???
    	{
    		r.v[i] -= V.v[i];
    	}
    	
    	return std::move(r);
    }
    Errare è umano, perseverare è diabolico.
    Quindi non posso lasciarle tutte e quattro? (Il costruttore di copia, il costruttore di spostamento, l'operatore di affezione e l'operatore "di spostamento".)

    Comunque il range-based for l'ho già corretto: mi sono accorto che non era il modo giusto di utilizzarlo!
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  6. #6
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Originariamente inviato da RooccoXXI
    Quindi non posso lasciarle tutte e quattro? (Il costruttore di copia, il costruttore di spostamento, l'operatore di affezione e l'operatore "di spostamento".)
    Certo che puoi, ma l'operazione di move dev'essere esplicita, altrimenti è implicita la copia.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  7. #7
    Originariamente inviato da shodan
    Certo che puoi, ma l'operazione di move dev'essere esplicita, altrimenti è implicita la copia.
    Quindi per utilizzare l'operatore di affezione con la move semantics devo fare

    codice:
    v1 = std::move(v2)
    ? Scusa ma sono un po' confuso... xD. Se invece non ci fosse l'operatore normale (che però mi serve, ovviamente) basterebbe utilizzare la sintassi tradizionale?

    EDIT: Mmmmh. Sto cominciando a capite! =). Su internet si parla praticamente solo dell'operatore di affezione e del costruttore di copia (almeno dove ho letto io). Ma teoricamente posso creare un move constructor per ogni costruttore che ho fatto, no? Potrei quindi, se ho un std::vector temporaneo che non mi serve più e che ho già creato:

    codice:
    std::vector<double> V({0,1,2,3});
    Vector v1(std::move(V))
    Per riassumere: prevedo sia i costruttori che quelli con la move semantics. E questi ultimi li invoco esplicitamente utilizzando la funzione std::move quando li ho bisogno, giusto?
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  8. #8
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    EDIT:
    E questi ultimi li invoco esplicitamente utilizzando la funzione std::move quando li ho bisogno, giusto?
    Si: la move semantics è esplicita. Se non usi std::move, il compilatore userà il costruttore di copia e/o l'operatore di assegnamento che auto genera. Solo se esiste una reale rvalue il compilatore userà in automatico la move semantics.
    codice:
    class Test {
    	int i;
    	public:
    		Test() : i(0) {};
    		Test(int k) : i(k) {};
    
    		Test(const Test& rhs) : i(rhs.i) {}; // cctor
    
    		Test(Test&& rhs) : i(rhs.i) { // mctor
    			rhs.i=0;
    		};
    };
    
    Test g() {
    	Test t(10);
    	return t;
    }
    
    Test t = g(); // 1
    Test k = t;   //  2
    In (1) viene generata una rvalue dato che il compilatore deve creare una variabile temporanea di tipo Test come valore di ritorno. Pertanto sceglie in automatico il mctor.

    In (2) invece non esplicitando la move semantics hai due lvalue, di conseguenza il compilatore dovrà scegliere il cctor.

    Se commenti il cctor, il compilatore lo genera in automatico, se lo rendi privato (o delete nel tuo caso), il compilatore darà errore.
    Se commenti il mctor, disabiliti la move semantics e il compilatore dovrà scegliere il cctor (o generarne uno se non lo hai disabilitato).

    Il caso (1) è il tipico esempio di rvalue automatica, in tutti gli altri casi la move semantics deve essere esplicita.

    Essendo uno dei cardini del C++11 è meglio capirla bene per trarne il massimo vantaggio.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  9. #9
    Originariamente inviato da shodan
    EDIT:


    Si: la move semantics è esplicita. Se non usi std::move, il compilatore userà il costruttore di copia e/o l'operatore di assegnamento che auto genera. Solo se esiste una reale rvalue il compilatore userà in automatico la move semantics.
    codice:
    class Test {
    	int i;
    	public:
    		Test() : i(0) {};
    		Test(int k) : i(k) {};
    
    		Test(const Test& rhs) : i(rhs.i) {}; // cctor
    
    		Test(Test&& rhs) : i(rhs.i) { // mctor
    			rhs.i=0;
    		};
    };
    
    Test g() {
    	Test t(10);
    	return t;
    }
    
    Test t = g(); // 1
    Test k = t;   //  2
    In (1) viene generata una rvalue dato che il compilatore deve creare una variabile temporanea di tipo Test come valore di ritorno. Pertanto sceglie in automatico il mctor.

    In (2) invece non esplicitando la move semantics hai due lvalue, di conseguenza il compilatore dovrà scegliere il cctor.

    Se commenti il cctor, il compilatore lo genera in automatico, se lo rendi privato (o delete nel tuo caso), il compilatore darà errore.
    Se commenti il mctor, disabiliti la move semantics e il compilatore dovrà scegliere il cctor (o generarne uno se non lo hai disabilitato).

    Il caso (1) è il tipico esempio di rvalue automatica, in tutti gli altri casi la move semantics deve essere esplicita.

    Essendo uno dei cardini del C++11 è meglio capirla bene per trarne il massimo vantaggio.
    Grazie mille!!! Credo che pian piano sto capendo! ; ). Mi ci vorrà ancora un po' di pratica però.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

  10. #10
    Ho aggiunto i costruttore seguente:
    codice:
    Vettore::Vettore(std::vector<double>&& v)
    : v(std::move(v))
    {}
    Per invocare il move constructor a partire da un std::vector che non mi servirebbe più. Ciò ha senso perché muovo un std::vector "verso" un'altro (il mio attributo). Ma ha senso prevedere il move constructor per le std::initializer_list? A mio avviso no... Perché il mio attributo (std::vector) é costruito a partire da esse, dunque un move non avrebbe senso. Ma non si sa mai... ; )

    codice:
    Vettore::Vettore(std::initializer_list<double>&& l)
    : v(std::move(l))
    {}
    Grazie,
    R.
    K. L. Thompson
    You can't trust code that you did not totally create yourself.
    A. Bogk
    UNIX is user-friendly, it just chooses its friends.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.