2010-12-17 6 views
4

Désolé pour le titre long.Pourquoi le compilateur ne peut-il pas différencier typedef et non-typedef?

J'ai un typedef dans une liste de classe:

template <typename T> 
class List { 
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList 
    typedef const Iter_ const_iterator; 

    const_iterator cbegin() const; 
}; 

et la définition en dehors de la classe, mais à l'intérieur du fichier d'en-tête.

template <typename T> 
typename List<T>::const_iterator List<T>::cbegin() const {} 

Cela produit le C2373 d'erreur: Redefinition; different type modifiers

Je réécris la fonction comme ceci:

template <typename T> 
const typename List<T>::Iter_ List<T>::cbegin() const {} 

et l'erreur a disparu; le programme compile correctement. (Repensez le fait que je ne retourne rien dans ces exemples, c'est sans importance pour l'exemple.)

Quelle est l'interprétation du compilateur avec la version erronée qui empêche la compilation réussie que la deuxième version ne fonctionne pas, et comment puis-je remédier à cela?

Plus code

J'utilise VS2008

Le (plus complète) exemple de code Je programme actuellement:

template <typename T> 
class List 
{ 
public: 
    // Forward declaration. 
    class Iter_; 

private: 
    ///////////////////////////////////////////////////////// 
    // ListElem 
    ///////////////////////////////////////////////////////// 
    struct ListElem 
    { 
     T data; 
     // Doubly-linked list. 
     ListElem *next; 
     ListElem *prev; 
    }; 

    class ListException {}; 

    //////////////////////////////////////////////////////// 
    // List Members 
    //////////////////////////////////////////////////////// 
    // Point to first elem. 
    ListElem *head_; 
    // Point to last elem. 
    ListElem *tail_; 

public: 
    ////////////////////////////////////////////////////////// 
    // Typedefs 
    ////////////////////////////////////////////////////////// 
    typedef  Iter_ iterator; 
    typedef const Iter_ const_iterator; 

    ////////////////////////////////////////////////////////// 
    // Iterator class 
    ////////////////////////////////////////////////////////// 
    class Iter_ 
    { 
    public: 
     Iter_(ListElem *pCurr, List *pList) 
      : pCurr_(pCurr), pList_(pList) 
     {  } 

     T& operator*() 
     { 
      if(*this == pList_->end()) 
       throw ListException(); 
      else 
       return pCurr_->data; 
     } 

    private: 
     ListElem *pCurr_; 
     List  *pList_; 
    }; 

iterator begin(); 
iterator end(); 

const_iterator cbegin() const; 
const_iterator cend() const; 
}; 

template <typename T> 
List<T>::List() 
    : head_(0), tail_(0), size_(0) 
{ } 

template <typename T> 
List<T>::~List() 
{ 
    //this->clear(); 
} 

template <typename T> 
List<T>::List(List const& other) 
    : size_(other.size_) 
{ 
    //this->clone(other); 
} 

template <typename T> 
List<T>& List<T>::operator=(List const& other) 
{ 
    size_ = other.size_; 
    //this->clone(other); 
} 

// Compiles ok 
template <typename T> 
typename List<T>::iterator List<T>::begin() 
{ 
    if(!head_) 
     head_ = new ListElem(); 
    return iterator(head_, this); 
} 

// Compiles ok 
template <typename T> 
typename List<T>::iterator List<T>::end() 
{ 
    return iterator(tail_, this); 
} 

// Compiler error 
template <typename T> 
typename List<T>::const_iterator List<T>::cbegin() const 
{ 
    return const_iterator(head_, this); 
} 

// Compiles ok 
template <typename T> 
typename const List<T>::Iter_ List<T>::cend() const 
{ 
    return const_iterator(tail_, this); 
} 
+0

Où (et comment) 'Iter_' est-il défini? – atzz

+1

comeau le compile juste bien (sauf pour 'typename const Liste :: Iter_ etc ...' qui devrait être 'const typename etc ...') – lijie

+0

@atzz: J'ai ajouté le code supplémentaire. – IAE

Répondre

2

L'erreur que je reçois lors de l'instanciation cbegin() est que vous transmettez (const) this au constructeur qui prend un pointeur non-const dans la liste. Fondamentalement, je doute que cette idée fonctionne si bien.

typedef const Iter_ const_iterator; 
+1

+1: en effet, ce n'est pas ** ce qu'est un 'const_iterator'. Il est implémenté comme un type différent car il nécessite des opérations mutables telles que 'operator ++'. –

+0

Merci pour la clarification! Je courais dans l'erreur que vous avez mentionnée quand j'ai changé la définition de la fonction à la fonctionnelle (pour moi sur VS2008). – IAE

1

Le code:

class Iter_ 
{ 
}; 

template <typename T> 
class List { 
public: 
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList 
    typedef const Iter_ const_iterator; 

    const_iterator cbegin() const; 
}; 

template <typename T> 
typename List<T>::const_iterator List<T>::cbegin() const {} 

int main() 
{ 
    List<int> foo; 

    List<int>::const_iterator iter = foo.cbegin(); 

    return 0; 
} 

compile juste bien avec gcc 4.2.2 (que j'admets est vieux).

Cependant, il semble qu'il existe une définition en double réelle dans vos fichiers que vous avez supprimés lorsque vous avez modifié le type à Iter_ à la place. Pourriez-vous nous donner un exemple de code complet qui ne parvient pas à compiler avec le message d'erreur?

EDIT: J'ai encore essayé avec votre plus grand exemple. Il y avait une tonne d'erreurs (suppressions de fonction manquantes et size_ manquante) que j'ai corrigé.

Après cela, cbegin bien compilé en cend n'a pas, parce que vous avez écrit typename const List<T>::Iter_ List<T>::cend() const au lieu de const typename List<T>::Iter_ List<T>::cend() const (const est pas partie de la chose qualifiée avec typename).

Si cbegin est vraiment celui qui génère une erreur, cela ressemble à un bug du compilateur.

+0

J'ai ajouté le code que j'utilise. – IAE

1

Ce:

// Compiler error 
template <typename T> 
typename List<T>::const_iterator List<T>::cbegin() const 
{ 
    return const_iterator(head_, this); 
} 

avec g ++ compile. Mais ce n'est pas le cas:

// Compiles ok 
template <typename T> 
typename const List<T>::Iter_ List<T>::cend() const 
{ 
    return const_iterator(tail_, this); 
} 

Pouvez-vous vérifier que vous avez correctement étiqueté votre code?

+0

Oui, j'ai. J'utilise VS2008, donc ça pourrait faire la différence. – IAE

1

je peux compiler votre code dans VS2008 si je fais le changement suivant:
template <typename T>
typename const List<T>::const_iterator List<T>::cbegin() const
Je ne sais pas pourquoi ce const supplémentaire devrait faire la différence, mais je parie que quelqu'un fait.

+0

Cela ne devrait pas faire de différence ici. Cela ressemble à un bug du compilateur. – aschepler

1

Edit:

Comment très étrange. J'ai même utilisé la déduction de type automatique pour obtenir le type de retour correct, et il a toujours rejeté mon code.

template <typename T> 
decltype(List<T>().cbegin()) List<T>::cbegin() const 
{ 
    return const_iterator(head_, this); 
} 

Bien sûr, votre code mis en ligne a un tas de fonctions que vous avez définies mais ne déclare pas, comme opérateur =, les constructeurs, destructor, qui a jeté des erreurs pour moi. Il fonctionne aussi parfaitement correctement s'il est implémenté en ligne. Cela pue comme un bug du compilateur pour moi.

1

test sur ce GCC 4.5.0 (MinGW), le code suivant compile bien:

template <typename T> 
class List { 
    public: 
    class Iter_ {}; 
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList 
    typedef const Iter_ const_iterator; 

    const_iterator cbegin() const; 
    const_iterator cend() const; 
}; 
template <typename T> 
typename List<T>::const_iterator List<T>::cbegin() const {} 
template <typename T> 
const typename List<T>::Iter_ List<T>::cend() const {} 

Si je change la dernière ligne

typename const List<T>::Iter_ List<T>::cend() const {} 

il ne compilera pas . Mark a donné une excellente explication à cela, typename List<T>::Iter est une chose qui ne devrait pas être séparée en insérant des modificateurs de type aléatoires à l'intérieur. Cela fonctionne aussi bien:

typename List<T>::Iter_ const List<T>::cend() const {} 

comportement GCC fait sens pour moi, donc je pense que ce bogue dans le compilateur MSVC.

1

Essayons de réduire cela le plus simplement possible. Est-ce que MSVC 2008 compile ce fichier?

template <typename T> class L { 
public: 
    class I {}; 
    typedef const I C; 
    C f() const; 
}; 

template <typename T> typename L<T>::C L<T>::f() const { return C(); } 

int main() { 
    L<int> x; 
    x.f(); 
    return 0; 
} 

Sinon, vous avez une petite démonstration d'un bug du compilateur!

+0

Ne compile pas; mais je pense que tu as déjà essayé ça? Merci pour l'exemple. – IAE

+0

Eh bien, je n'ai pas MSVC 2008. Je l'ai essayé avec ma version g ++ et cela a fonctionné. – aschepler

Questions connexes