NSA - Non Solo Amiga

SOFTWARE => Linguaggi di programmazione e scripting => Topic aperto da: clros - 13 Novembre 2011, 17:53:15

Titolo: [C++] overload dell'operator[]
Inserito da: clros - 13 Novembre 2011, 17:53:15
Ciao a tutti,

volendo creare una classe container che consenta di mantenere gli elementi in base all'ordine di inserimento, stavo cercando di modificare il listato suggeritomi da TheKaneB (http://http://www.nonsoloamiga.com/forum/viewtopic.php?f=12&t=950).

Però, invece del metodo put (
Codice: [Seleziona]
T* put(K key, T value) ), pensavo di usare l'operator[] (array subscript) per fare in modo da poter accedere o scrivere sul mio container.
Ho tentato di effettuare un overload di tale operatore così:

Codice: [Seleziona]
T& operator[] ( const K& x )
  {
    T& result = myMap[x] = 4; //myMap is a std::map
    std::cout<<"Operatore [], passato KEY:"<<x<<endl;
    return result;
  }

il fatto è che quando istanzio il mio oggetto, poi vorrei usare tale operatore nella consueta maniera:

Codice: [Seleziona]
myContainer[key] = value;
alla funzione membro dell'operatore arriva il valore di key (x nella mia funzione), ma come fare a risualire anche al valore (valore che, per prova, nella mia funzione, vale 4)?
Avevo pensato che dovrei anche coinvolgere l'operator=, ma è corretto? (cmq non ci sono riuscito...)
Titolo: Re: [C++] overload dell'operator[]
Inserito da: Z80Fan - 13 Novembre 2011, 22:48:09
(Scrivo senza avere fatto prove, quindi prendere ogni informazione con le pinze)

Citazione da: "clros"
Ho tentato di effettuare un overload di tale operatore così:

Codice: [Seleziona]
T& operator[] ( const K& x )
  {
    T& result = myMap[x] = 4; //myMap is a std::map
    std::cout<<"Operatore [], passato KEY:"<<x<<endl;
    return result;
  }

il fatto è che quando istanzio il mio oggetto, poi vorrei usare tale operatore nella consueta maniera:

Codice: [Seleziona]
myContainer[key] = value;
Prima di tutto, quell'assegnazione (T& result = myMap

Cmq tutto quello che dovresti fare nella funzione è ritornare un riferimento all'elemento; quando hai fatto quello, è il compilatore che si arrangia a usarlo nel modo che serve (se leggerlo o scriverlo).
Banalmente:

Codice: [Seleziona]
int vettore[10];

int & operator[] ( const int &x )
{
    return &(vettore[x]);
}
Poi che sia un l-value o un r-value non dovrebbe essere un problema, perchè tanto il riferimento non è costante quindi puoi fare quello che vuoi.
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 25 Novembre 2011, 23:42:56
Allora, questo è lo scheletro della mia classe:

Codice: [Seleziona]
template <typename K, typename V>
class Insert_Order_Map : public std::map<K,V>
{
 
};

Insert_Order_map<string,int> myMap;
myMap["primo"] = 1;
//...

In questa classe (derivata da std::map) vorrei fare in modo di intercettare l'operatore [] (quando inserisco un elemento nella mia mappa) in modo da aggiornare una lista (std::list o std::vector) che tenga memorizzati gli elementi secondo la sequenza di inserimento. Come fare? :shock:

Di sicuro dovrei sovrascrivere l'operatore[] ma non so come procedere :( (ovviamente non ho problemi per l'aggiornamento della lista che effettuerei nella riscrittura dell'operatore)

TheKaneB aveva suggerito un metodo put:

 
Citazione
T* put(K key, T value)
   {
      DictionaryType::iterator it = mDictionary.find(key);

      if (it == mDictionary.end() )
      {
         T* tmp = &(mDictionary[key] = value);
         mHistory->push_front(tmp);
         return tmp;
      }

Penso che vada bene, solo che dovrei riscriverlo nell'operatore[]
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 26 Novembre 2011, 01:26:08
Ok, apparentemente ci sono riuscito (ho scopiazzato il codice direttamente dall'header <map>  :D

Codice: [Seleziona]
V& operator[]( const K&& key )
  {
    typename std::map<K,V>::iterator __i = find(key);
if (__i == std::map<K,V>::end()) //se non l'ho trovato...
{
      __i = insert(__i, std::make_pair(std::move(key), V()) );
    }
return (*__i).second;
  }

Ads quello che proprio non riesco a capire è la parte il secondo parametro del make_pair; praticamente cos'è quel V() ??? :think:
V nel mio caso è il nome di un tipo della mia classe template:

Codice: [Seleziona]
template <typename K, typename V>
class FJM_Map : public std::map<K,V>
{
 //...
Titolo: Re: [C++] overload dell'operator[]
Inserito da: TheKaneB - 26 Novembre 2011, 15:27:24
semplicemente crea una nuova entry nella mappa, nello slot di nome key (di tipo K) con il valore di default del tipo V (value).

Il valore di default è dato dal costruttore di default V().

Questo significa che quando scrivi V tmp = mappa["mianonna"], la variabile tmp avrà SEMPRE un valore definito.
Se l'entry "mianonna" non era presente nella mappa, allora ti verrà creata un'entry nuova con il suo valore di default.
Ecco perchè è sempre bene usare il metodo find() quando si fanno ricerche nelle mappe, altrimenti hai 2 effetti collaterali:
- inspiegabilmente l'elemento che cerchi sarà sempre presente (per forza, te lo crea al volo!)
- aumenta la memoria occupata senza motivo, facendo esplodere l'applicazione anche per delle semplici ricerche.

Quindi usa sempre il find() per trovare un elemento e [] soltanto quando vuoi inserirne uno.
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 26 Novembre 2011, 16:29:43
OKKey!
Ma ads c'è un altro problema (l'ultimo?)
Questo è il codice della mia classe più il main:

Codice: [Seleziona]
template <typename K, typename V>
class FJM_Map : public std::map<K,V>
{
 public:

 V& operator[]( const K&& key )
  {
   //Come già riportato in altro post
  }

  typename std::map<K,V>::iterator cbegin() const
  {
        return *mHistory.cbegin();
  }

  typename std::map<K,V>::iterator cend() const
  {
      return *mHistory.cend();
  }

  private:
    std::list<typename std::map<K,V>::iterator> mHistory;
};

using namespace std;

int main()
{
    FJM_Map<string,int> myMap;

    myMap["1"] = 1;
    myMap["23"] = 23;
    myMap["-3"] = -3;
    myMap["23"] = 28;
    myMap["ultimo"] = 33;

  //  std::cout<<myMap.cbegin()->first;
    for (auto x = myMap.cbegin(); x != myMap.cend(); ++x)
        cout<<(*x).first <<"t"<<x->second<<endl;
     return 0;



Il mio scopo è quello di visualizzare il contenuto della std::list, invece che quello della mappa, sovrascrivendo i metodi cbegin() e cend() di std::map (in pratica passo al chiamante gli iteratori di inizio e di fine della lista invece che della mappa).

Ecco, il for, dopo aver visualizzato alcuni elementi della mappa, va in segmentation fault  :cry:
Titolo: Re: [C++] overload dell'operator[]
Inserito da: TheKaneB - 26 Novembre 2011, 16:46:04
evidentemente qualcuno degli elementi della lista punta chissà dove (magari a end() oppure non è inizializzato, oppure è un puntatore nullo).
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 26 Novembre 2011, 16:53:13
Citazione da: "TheKaneB"
evidentemente qualcuno degli elementi della lista punta chissà dove (magari a end() oppure non è inizializzato, oppure è un puntatore nullo).

Certo...però è strano, la lista la aggiorno solo quando inserisco un elemento nella mappa:


Codice: [Seleziona]
V& operator[]( const K&& key )
  {
    typename std::map<K,V>::iterator __i = find(key);
if (__i == std::map<K,V>::end()) //se non l'ho trovato...
{
           __i = insert(__i, std::make_pair(std::move(key), V()) );
          mHistory.push_back(__i);
          std::cout<<"Sto inserendo: "<<key<<std::endl;
    }
return (*__i).second;
  }
Titolo: Re: [C++] overload dell'operator[]
Inserito da: TheKaneB - 26 Novembre 2011, 16:58:46
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 26 Novembre 2011, 17:13:24
Citazione da: "TheKaneB"
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

E' quello che ho pensato anche io mentre stavo per andare in doccia...la map usa un qualche tipo di albero che, per mantenere bilanciato, rimescola i dati ad ogni inserimento!!  :cry:
Mettendo un puntatore al Value, poi potrò fornire lo stesso iteratore in output a cbegin() e cend() ?

Citazione
Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.

Funzionano...il problema è sempre nel metodo insert(), vero?
Titolo: Re: [C++] overload dell'operator[]
Inserito da: clros - 26 Novembre 2011, 17:13:44
Citazione da: "TheKaneB"
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

E' quello che ho pensato anche io mentre stavo per andare in doccia...la map usa un qualche tipo di albero che, per mantenere bilanciato, rimescola i dati ad ogni inserimento!!  :cry:
Mettendo un puntatore al Value, poi potrò fornire lo stesso iteratore in output a cbegin() e cend() ?

Citazione
Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.

Funzionano...il problema è sempre nel metodo insert(), vero?