PDA

Visualizza la versione completa : [C++] problemi di compilazione con std::list<T>::iterator


MegaAlchimista
14-01-2013, 15:46
Avendo codice come questo


template<class T>
std::list<T>::iterator nomefunzione()
{
std::list<T> _list;
//.. codice
return _list.begin();
}

ottengo un errore dal compialtore (sto lavorando con visual studio 2012)

warning C4346: 'std::list<T>::iterator': il nome dipendente non è un tipo

Andando in giro a cercare su internet ho scoperto che per nona vere questo errore devo sempre premettere la keyword typename .
Effettivamente riscrivendo la funzione come segue non ottengo errori:


template<class T>
typename std::list<T>::iterator nomefunzione()
{
std::list<T> _list;
//.. codice
return _list.begin();
}


Francamente l'unica spiegazione che ho trovato mi risulta aimè molto ostica e non riesco realmente a comprenderla

The point is that you have a template that uses another template based on its own template parameters. That means that if e.g. the other class is specialised for a certain type, it could change the whole interface when compared to the general template, including sometimes twisting around the meaning of things.
Example: under weird circumstances, e.g. list<weird_thing>::iterator might
in fact refer to a static member. Not that there is any reason to do just
that with list<>, but it's not up to the compiler to reason. Therefore,
when referring to a nested type or typedef, you need to prefix it
with 'typename' in this context.

Inoltre ho un altro problema:
Nel file che sto scrivendo list::iterator appare in numerosi punti, avevo quindi pensato di usare una comoda typedef, come potrebbe essere questa

template<class T> typedef std::list<T>::iterator listIterator<T>;
ovviamente non compila e da lo stesso problema di cui sopra.. in questo caso non posso però premettere la keyword typename : esistono altri metodi per ottenere lo stesso risultato?

MItaly
14-01-2013, 16:29
Originariamente inviato da MegaAlchimista
Francamente l'unica spiegazione che ho trovato mi risulta aimè molto ostica e non riesco realmente a comprenderla
Come spiegato qui (http://stackoverflow.com/a/642447/214671), il punto è che la "compilazione" di un template avviene in due fasi: c'è una prima verifica sintattica "di base", quindi, nel momento in cui viene effettivamente istanziato (specificando un tipo), avviene la compilazione vera e propria.

Ora, nella prima fase vengono verificati diversi aspetti sintattici fondamentali, senza però sapere ancora esattamente che tipo verrà passato come parametro template.
In questo passaggio, tuttavia, qualunque espressione del tipo tipo<T>::qualcosa è sostanzialmente ambigua, dato che, anche se nella definizione di base di tipo<T> qualcosa è un typedef o in generale rappresenta un tipo, nulla impedisce che una specializzazione particolare della classe definisca qualcosa come, ad esempio, campo statico (al quale ci si riferisce con la medesima sintassi, ma ha ovviamente una semantica completamente diversa).
Per questo motivo, lo standard impone che, durante questo primo passaggio, tutti gli identificatori del tipo tipo<T>::qualcosa vengano considerati a priori come un valore, e non come un tipo. Da questo il messaggio di errore: iterator è il "nome dipendente" (dall'effettiva definizione di std::list<T>) e non viene considerato come un tipo, ma come un valore.

Per specificare al compilatore che invece il nome dipendente a cui ti riferisci è un tipo (e dunque è ammissibile usarlo come tipo restituito per la tua funzione) si usa la keyword typename, che forza l'interpretazione del nome dipendente come tipo.


Inoltre ho un altro problema:
Nel file che sto scrivendo list::iterator appare in numerosi punti, avevo quindi pensato di usare una comoda typedef, come potrebbe essere questa

template<class T> typedef std::list<T>::iterator listIterator<T>;
ovviamente non compila e da lo stesso problema di cui sopra.. in questo caso non posso però premettere la keyword typename : esistono altri metodi per ottenere lo stesso risultato?
Il problema è che non è possibile avere dei typedef con parametri template (o meglio, puoi avere un typedef all'interno di una classe template, ma non puoi avere un typedef "a sé stante" parametrizzato con un parametro template); tuttavia, in C++11 (l'ultima revisione dello standard) è possibile fare qualcosa del genere con la keyword using.
http://en.wikipedia.org/wiki/C%2B%2B11#Alias_templates

MegaAlchimista
14-01-2013, 17:52
Originariamente inviato da MItaly
[...]
Il problema è che non è possibile avere dei typedef con parametri template (o meglio, puoi avere un typedef all'interno di una classe template, ma non puoi avere un typedef "a sé stante" parametrizzato con un parametro template); tuttavia, in C++11 (l'ultima revisione dello standard) è possibile fare qualcosa del genere con la keyword using.
http://en.wikipedia.org/wiki/C%2B%2B11#Alias_templates

Innanzitutto grazie per la risposta chiara ed esaustiva.
Andiamo al problema della typedef.
Premesso che il mio compilatore supporta lo standard C++11 e che già utilizzo alcune delle sue funzionalità (come le enum class o nullptr o i thread ...) , non riesco a "far funzionare" la keyword using.
Il seguente è stato il mio approccio:
-Utilizzando già thread, enum class e compagnia bella ho dato per scontato di avere accesso a tutte le funzionalità dell'ultimo standard.
Sono partito prendendo direttamente l'esempio pratico di wikipedia


template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
using TypedefName = SomeType<OtherType, Second, 5>;

questo non va bene per il mio caso , infatti scrivendo semplicemente


template<typename T> using list_iterator = std::list<T>::iterator;
//errore di sintassi 'using'


Sul momento ho pensato che l'errore fosse legato a tutto ciò che abbiamo appena detto, ed ho provato semplicemente ad aggiungere typename


template<typename T> using list_iterator = typename std::list<T>::iterator;

Niente da gli stessi errori anche così.

Così ho fatto qualche ricerca e su StackOverFlow (http://stackoverflow.com/questions/11631919/c11-using-keyword-specialize-template-alias-of-template-parameter) ho trovato questo


template <class A>
struct Y
{
using Left = typename A::Left;
using Right = typename A::Right;
using AReverse = typename A::Sibling<Right, Left>; // Gives a compiler error
};
//dice che essendo Sibling un template bisogna scrivere in questo modo
using AReverse = A::template Sibling<Right, Left>;

Allora ho tentato di applicare questo concetto al mio caso , pensando potesse funzionare


template<typename T>
using list_iterator = typename std::list<T>::iterator;
// l'ho riscritto come:
template<typename T>
using list_iterator = std::list<T>::typename iterator;

sempre gli stessi errori.. così mi è sorto un dubbio, ed ho provato a scrivere:


template<typename T> using lista = std::list<T>;
//error C2988: dichiarazione/definizione di modello non riconoscibile
//error C2059: errore di sintassi: 'using'

Finalmente ho ben pensato di leggere quali fossero gli errori:
"dichiarazione di modello non riconoscibile"? Non è che non supporta lo standard?
Infatti non compila nemmeno il caso più semplice di tutti


using listaIntera = std::list<int>;

qui però da errori diversi


Errore 1 error C2143: errore di sintassi: ';' mancante prima di '='
Errore 2 error C2873: 'listaIntera': il simbolo non può essere utilizzato in una dichiarazione using
Errore 3 error C2513: 'int': nessuna variabile dichiarata prima di '='

ho come l'impressione che non supporti C++11, eppure questo mi sembra impossibile dato che (come già detto) uso tante altre "novità" introdotte nello standard.

Oppure (probabile) non ho capito niente sul suo utilizzo

MegaAlchimista
14-01-2013, 18:24
Risolto l'arcano: non è supportato in visual studio come scritto sulla documentazione msdn (http://msdn.microsoft.com/en-us/library/vstudio/hh567368.aspx) andando a vedere la voce Alias Templates .

(Questa cosa devo dire che mi infastidisce alquanto)

shodan
15-01-2013, 12:33
Herb Sutter ha annunciato che verso aprile sarà distribuito un aggiornamento del compilatore (presumo un service pack) per allineare il compilatore e relativa libreria al pieno supporto C++11.
Nel frattempo è stato distribuito un aggiornamento di test (il VC++ CTP) che supporta una parte della uniform initialization sintax e i variadic template. Tuttavia è solo il compilatore che supporta le nuove features, per la libreria dovremo aspetta ancora un po'.

Loading