Newsgroup: it.comp.lang.c++
Titolo: Eliminare un elemento da un vector

Salve,
non capisco perché questo codice dia errore:
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> v;
template <class C>
void elimina(C c) {
     for (std::vector<C>::iterator it = v.begin(); it != v.end(); it++)
         if (*it == c) {
            v.erase(it);
            break;
         };
     };
int main() {
    v.push_back("A");
    v.push_back("B");
    elimina<std::string>("B");
    return 0;
}
Dice che it non è dichiarato... se tolgo i template funziona. 

Ho provato questo codice:
#include <iostream>
#include <vector>
std::vector<int> v;
void elimina(int c) {
     for (int i = 0; i <  v.size(); i++)
         if (v[i] == c) {
          std::remove (v.begin(), v.end, v[i]);
            break;
         };
     };
Ma non trova la funzione remove... 

std::remove (v.begin(), v.end, v[i]);
ho dimenticato le parentesi in v.end()
Ma il problema del post precedente rimane

Noixe ha scritto:

prmetto che di C++ non me ne intendo tantissimo, mi sono iscritto su
questo ng per imparare qualcosa id più.
Paragonando la situazione a una identica in java, anche l'iterator ha il
template.
Quindi dovrebbe essere
std::vector<C>::iterator<C> it
spero sia lo stesso anche quì :)

 }
//    elimina<std::string>("B");
    std::string B( "B" );
    elimina<std::string>(B);
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 Dice che it non è dichiarato... se tolgo i template funziona.
Si tratta di un problema classico. In certi contesti, quando la 
dichiarazione di un tipo dipende da un parametro template (C in questo 
caso) il compilatore non è in grado di capire se ti stai riferendo ad un 
tipo o ad una funzione. In questi casi devi utilizzare la keyword 
typename per indicargli che si tratta di un tipo:
for (typename std::vector<C>::iterator it = v.begin(); it != v.end(); it++)
Ciao.

 it++)
Sono sicuro di averlo provato ma dava errore, tuttavia ora l'ho  messo e 
funziona... staro' diventado cieco? :-)
Grazie 

 std::vector<C>::iterator<C> it
Mi pare che in C++ il template lo deduca da quello utilizzato per il 
contenitore (vector in questo caso) 

    elimina<std::string>(B);
Richiede sempre il typename 

Ecco l'avevo provato nel programma orignale e non funzionava ecco perché 
l'ho scartato.
In pratica, stavo provando a fare una semplice classe per simulare gli array 
associativi, ma il metodo "elimina" da problemi.
#include <iostream>
#include <string>
#include <vector>
template <class C, class V>
class Mappa {
      public:
             V& operator[] (C c) {
                for (int i = 0; i < ac.size(); i++) {
                   if (ac[i] == c)
                     return av[i];
                };
                ac.push_back(c);
                av.push_back(V());
                return av[av.size() - 1];
             };
             void inserisci (C c, V v) {
                  int i = 0;
                  for (; i < ac.size(); i++)
                      if (ac[i] == c) {
                         av[i] = v;
                         break;
                      };
                  if (i >= ac.size()) {
                     ac.push_back(c);
                     av.push_back(v);
                  };
             };
             void elimina(C c) {
                  for (typename std::vector<C>::iterator it = ac.begin(); it 
!= ac.end(); it++)
                      if (*it == c) {
                         ac.erase(it);
                         av.erase(it);
                      };
             };
      private:
              std::vector<C> ac;
              std::vector<V> av;
};
int main() {
    Mappa<std::string, int> M;
    M.inserisci("A", 1);
    M.inserisci("B", 2);
    M["C"] = 3;
    M["D"] = 4;
    std::cout << M["A"] << std::endl;
    std::cout << M["B"] << std::endl;
    std::cout << M["C"] << std::endl;
    std::cout << M["D"] << std::endl;
    M.elimina("C");
    return 0;
}
Grazie 

 Richiede sempre il typename
Hai ragione, mi ero sbagliato. Comunque nel tuo programma c'è anche un punto 
e virgola di troppo dopo il ciclo for.


 punto e virgola di troppo dopo il ciclo for.
Puoi quotarmi la riga di codice a cui ti riferisci? 

 Puoi quotarmi la riga di codice a cui ti riferisci?
template <class C>
void elimina(C c) {
     for (std::vector<C>::iterator it = v.begin(); it != v.end(); it++)
         if (*it == c) {
            v.erase(it);
            break;
         };
     };  <-- questa



Ma cambia il senso del programma? 

 Ma cambia il senso del programma?
No, ma il gcc 4.2.3 me lo segnala come errore.


     };
Direi che non ti è chiaro il concetto di iteratore.
Un iteratore è un'astrazione di un puntatore ad un elemento di un 
contenitore e serve per evitare di preoccuparti dei dettagli 
implementativi del contenitore stesso permettendoti, entro certi limiti, 
di poter scrivere algoritmi che funzionino correttamente 
indipendentemente dal tipo di contenitore.
Dato che rappresenta un elemento all'interno di un contenitore e *non* 
una posizione all'interno dello stesso, non è possibile eliminare da un 
contenitore un iteratore appartenente ad un altro, come fai tu nel 
codice di cui sopra (av.erase(it)).
it è un iteratore che "appartiene" ad ac (come se fosse un puntatore ad 
un elemento di ac), rimuovere it da av quindi è un errore (dale 
conseguenze nefaste).
Per risolvere il problema devi avere anche un iteratore appartenente ad 
av ed inizializzato ad av.begin(), che venga incrementato ad ogni 
iterazione del ciclo for e rimuovere quello all'interno dell'if.
Devo dire però che la realizzazione di una mappa associativa in questo 
modo (con 2 vettori, uno per le chiavi ed uno per i valori) mi pare 
decisamente complicata e facilmente fonte di errori.
In realtà le mappe associative vengono implementate con altre strutture 
dati in modo da avere complessità computazionale minore.
Pur volendola implementare, per semplicità, con un "normale" vettore 
(già una lista sarebbe meglio in quanto ti permette di rimuovere 
elementi in qualsiasi posizione a costo costante), sarebbe molto più 
semplice utilizzare un unico vettore che contenga la coppia 
chiave-valore (std::vector<std::pair<C, V> >).
Ciao.

 Ma C come potrebbe essere una funzione?
Errore mio, volevo scrivere membro ed ho scritto funzione, però non si 
riferisce a C (ed infatti non ho scritto std::vector<typename 
C>::iterator), ma a std::vector<C>::iterator, che il compilatore non può 
sapere se è un tipo od un membro.
Ciao.

 (std::vector<std::pair<C, V> >).
Beh, l'idea era di implementarla usando solo elementi base del linguaggio, 
infatti usavo gli array prima di introdurre i vector.. 

"Sergio" <ser...@invalid.invalid> ha scritto:

Si ma per quale valore di C ad esempio
std::vector<C>::iterator  può essere scambiato per un membro di 
std::vector<C> ?
Essendo seguito dall'identificatore "it", non è evidente che ciò che lo 
precede sia un tipo? 

 contorta :-)
L'unica sintassi che accetta è questa:
void elimina(C c) {
                  typename std::vector<C>::iterator itc;
                  typename std::vector<V>::iterator itv;
                  for (itc = ac.begin(), itv = av.begin(); itc != ac.end(); 
itc++, itv++)
                      if (*itc == c) {
                         ac.erase(itc);
                         av.erase(itv);
                      };
};
Strano che si possa inserire tutto nel for. 

> Strano che si possa inserire tutto nel for.
che NON si possa 

 che NON si possa
Il primo statement può essere un'espressione o una dichiarazione semplice.
Tu hai cercato di usare qualcosa che non è né una cosa né l'altra.
Max

 Tu hai cercato di usare qualcosa che non è né una cosa né l'altra.
Pensavo andasse bene qualsiasi dichiarazione 


Attenzione che 'erase' invalida gli iteratori. Bisogna usare una
costruzione del genere:
itc = ac.erase(itc);
Saluti

 infatti usavo gli array prima di introdurre i vector..
std::pair è una semplice struttura così fatta:
template <class F, class S> struct pair
{
     F first;
     S second;
};
Ciao.

 precede sia un tipo?
Ci sono casi in cui l'uso di un nome dipendente da un parametro template 
(come std::vector<C>) è ambiguo e casi in cui l'uso può essere corretto 
o errato a seconda del parametro. Questi richiedono l'uso di typename 
per essere chiariti. Poi ci sono casi in cui il significato è chiaro, ma 
per uniformità (ed anche per aiutare il compilatore) la regola si 
estende a tutti i casi nei quali si usa un nome dipendente.
Ti faccio qualche esempio:
struct A
{
     typedef int iterator;
};
struct B
{
     void iterator(int);
};
struct C
{
     static int iterator;
};
int it;
template <class T> void f()
{
     T::iterator(it); (1)
     T::iterator * it; (2)
     T::iterator it; (3)
}
(1) è ambiguo, perché potrebbe essere la dichiarazione della variabile 
it di tipo T::iterator (f<A>) oppure la chiamata delle funzione iterator 
con it come parametro (f<B>).
anche (2) è ambiguo, perché potrebbe essere la dichiarazione della 
variabile it di tipo puntatore a T::iterator (f<A>), ma potrebbe anche 
essere la moltiplicazione tra it ed il membro T::iterator (f<C>).
(3) poterebbe essere corretto (f<A>) o errato (f<B> o f<C>)
Il compilatore ha la necessità di risolvere le ambiguità (o la 
corretteza) nei punti (1), (2) e (3) ma è in grado di farlo solo 
all'atto dell'istanziazione dei template. Ecco che la keyword typename 
entra in gioco.
Spero di essere stato sufficientemente chiaro.
Ciao