2010-11-25 2 views
7

Faire un ami inconnuFaire une classe non définie comme ami, et définir plus tard

template<typename T> 
class List 
{ 
protected: 

    class a { 
     int x; 
     int y; 
    private: 
     friend class b; // <------------ Why this is not an error? 
    }; 

    template <typename U > class b { //If that is not a error this should be an error 
     int z; 
     U y; 
    }; 

    public: 
     List() { 
      a* ptr = (a *)new unsigned char[sizeof(a)]; 
     } 
}; 

int main() { 
    List<int> mylist; 
} 

S'il vous plaît passer par ce lien, j'ai mes questions sous forme de commentaires dans le code. J'essaie de faire d'une autre classe un ami de ma classe. Mais cette classe n'est pas connue au moment de se faire des amis. Quelle est la règle C++ qui le permet. Plus tard, je définis cette classe de telle sorte qu'elle est incompatible avec la déclaration d'un ami. Pourquoi ne pas jeter une erreur. Merci

Répondre

6

Oui votre code est invalide ! Ceci est une démonstration intéressante de la façon dont les modèles peuvent changer la signification du code de manière subtile. Le code suivant est valide:

class List 
{ 
public: 
    class a { 
     typedef int type; 
     friend class b; // that's fine! 
    }; 

    template <typename U > class b; 
}; 

class b { 
    List::a::type an_int; // allowed to access private member 
}; 

standard dit à 7.3.1.2/3

Si une déclaration d'ami dans une classe non-locale déclare d'abord une classe ou function83) la classe ami ou La fonction est un membre de l'espace de noms englobant le plus interne.

Quand est-ce une "première classe déclarée"? Lorsque vous recherchez une déclaration préalable d'une classe ou d'une fonction déclarée en tant qu'ami, et lorsque le nom de la classe ou de la fonction friend n'est ni un nom qualifié ni un ID de modèle, étendues en dehors de la portée de l'espace de noms englobant la plus interne ne sont pas pris en compte. La recherche de "classe b" est déléguée de 7.1.5.3/2 à 3.4.4 qui délègue à son tour la recherche de nom non qualifié à 3.4/7. Toute la question est maintenant de savoir si le nom de modèle "b" est visible dans la classe de déclaration d'ami a. Si ce n'est pas le cas, le nom n'est pas trouvé et la déclaration d'ami fera référence à une nouvelle classe déclarée à portée globale. 3.3.6/1 sur la portée de celui-ci dit

La portée potentielle d'un nom déclaré dans une classe se compose non seulement de la région déclarative suivante déclarateur est le nom, mais aussi de tous les corps de la fonction, les arguments par défaut, et le constructeur ctor- initialiseurs dans cette classe (y compris de telles choses dans les classes imbriquées).

Ignorant quelques points pédants qui rendraient cette formulation est pas applicable ici (qui étaient un défaut mais sont fixés dans la version C++ 0x de ce paragraphe qui rend également ce plus facile à lire), cette liste ne ne pas inclure la déclaration d'ami en tant que zone dans laquelle ce nom de modèle est visible.

Toutefois,, l'ami a été déclaré dans une classe membre d'un modèle de classe. Lorsque la classe membre est instanciée recherche différente s'applique - la recherche de noms d'amis déclarés dans un modèle de classe! La norme dit

Les classes ou fonctions d'amis peuvent être déclarées dans un modèle de classe. Lorsqu'un modèle est instancié, les noms de ses amis sont traités comme si la spécialisation avait été explicitement déclarée à son point d'instanciation.

Ainsi, le code suivant est incorrect:

template<typename T> 
class List 
{ 
public: 
    class a { 
     typedef int type; 
     friend class b; // that's fine! 
    }; 

    template <typename U > class b; 
}; 

// POI 
List<int>::a x; 

Quand cela provoque List<int>::a à instancier implicitement, le nom est a leva les yeux vers « // POI » comme s'il y aurait eu une spécialisation explicite déclaré. Dans ce cas, le modèle List::b a déjà été déclaré, et cette recherche l'atteindra et émettra une erreur car il s'agit d'un modèle et non d'une classe non-template.

+0

Votre analyse semble être correcte excepté 'Le nom de l'ami n'est pas trouvé par simple recherche de nom jusqu'à ce qu'une déclaration correspondante soit fournie dans cette étendue d'espace de noms (avant ou après la déclaration de classe accordant l'amitié). Votre code n'est pas compilé dans Comeau, Intel C++ et MSVC++. –

+0

@Prasoon Le texte que vous citez n'a aucune signification dans ce cas. Le nom de l'ami n'est pas recherché ici (le modèle est recherché). Les compilateurs ne définissent pas la norme, donc je ne discute pas avec les compilateurs acceptant ou rejetant. Que GCC et Clang l'acceptent est une bonne chose, mais ma réponse ne dépend pas d'eux, mais simplement du texte standard que je cite. L'interface EDG (qu'Intel et Comeau utilisent) et MSVC ont vraiment plus de bugs que ça, donc ça ne me surprend pas. –

+0

J'ai trouvé le texte un peu difficile à analyser. J'ai peut-être manqué quelque chose d'important. Ne t'en fais pas. Jetez aussi un coup d'œil à [cette question similaire] (http://stackoverflow.com/questions/4282662/template-friend-a-deadly-combination) et à un tas de réponses. –

2

Le code est mal formé et Comeau rejette donnant l'erreur suivante

error: invalid redeclaration of type name "b" (declared at 
     line 11) 

Je pense que cela est un bogue dans g ++. Intel C++ le rejette aussi. Vous pouvez corriger le code en définissant la classe B au-dessus de A.

template <typename U > class b { 
     int z; 
     U y; 
}; 
class a { 
     int x; 
     int y; 
    private: 
     friend class b<T>; 
}; 
+0

Merci. Mais la chose intéressante dans votre message est que sur les autres compilateurs c'est une erreur de redéclaration. Pour autant que je sache, la redéclaration n'est pas une erreur, ne devrait-elle pas avoir déclaré une déclaration contradictoire de type différent? – Saurabh

+1

@ user420536: Pas nécessairement. Sur MSVC++, j'obtiens 'List :: b ': le modèle hors classe a déjà été déclaré comme modèle de classe .' (erreur de redéclaration). Différents compilateurs sont autorisés à analyser le code différemment, ce qui leur permet d'émettre différents messages d'erreur. Je ne trouve rien de mal avec ce message d'erreur. ':)' –

3

// Exécuter this- il va maintenant compiler pour vous

template <typename U > class b; //<----- forward declaration 

template<typename T> 
class List 
{ 
protected: 


     class a { 
     int x; 
     int y; 
     private: 
      friend class b<T>; // <------------ Add <T> 
     }; 
     template <typename U > class b { 
      int z; 
      U y; 
     }; 

     public: 
     List() { 
      a* ptr = (a *)new unsigned char[sizeof(a)]; 
     } 
}; 

int main() { 
    List<int> mylist; 

} 
Questions connexes