Question

Dans l'exemple suivant (mes excuses pour la longueur) J'ai essayé d'isoler un comportement inattendu que j'ai rencontrés lors de l'utilisation des classes imbriquées dans une classe qui hérite d'un autre privé. Je l'ai souvent vu des déclarations à l'effet qu'il n'y a rien de spécial sur une classe imbriquée par rapport à une classe non imbriquées, mais dans cet exemple, on peut voir qu'une classe imbriquée (au moins selon GCC 4.4) peut voir les typedefs publics d'un classe qui est privée héritée par la classe de clôture.

Je comprends que typdefs ne sont pas les mêmes que les données membres, mais je trouve ce comportement surprenant, et j'imagine que beaucoup d'autres seraient aussi. Donc, ma question est double:

  1. Est-ce comportement standard? (Une explication décente pourquoi serait très utile)
  2. Peut-on espérer qu'il fonctionne sur la plupart des compilateurs modernes (à savoir, comment est-il portable)?

#include <iostream>

class Base {
  typedef int priv_t;
  priv_t priv;
public:
  typedef int pub_t;
  pub_t pub;
  Base() : priv(0), pub(1) {}
};

class PubDerived : public Base {
public:
  // Not allowed since Base::priv is private
  // void foo() {std::cout << priv << "\n";}

  class Nested {
    // Not allowed since Nested has no access to PubDerived member data
    // void foo() {std::cout << pub << "\n";}

    // Not allowed since typedef Base::priv_t is private
    // void bar() {priv_t x=0; std::cout << x << "\n";}
  };

};

class PrivDerived : private Base {
public:
  // Allowed since Base::pub is public
  void foo() {std::cout << pub << "\n";}

  class Nested {
  public:
    // Works (gcc 4.4 - see below)
    void fred() {pub_t x=0; std::cout << x << "\n";}
  };
};

int main() {

  // Not allowed since typedef Base::priv_t private
  // std::cout << PubDerived::priv_t(0) << "\n";

  // Allowed since typedef Base::pub_t is inaccessible
  std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0

  // Not allowed since typedef Base::pub_t is inaccessible
  //std::cout << PrivDerived::pub_t(0) << "\n";

  // Works (gcc 4.4)
  PrivDerived::Nested o;
  o.fred(); // Prints 0
  return 0;
}
Était-ce utile?

La solution

Avant-propos: Dans la réponse ci-dessous je me réfère à certaines différences entre 98 C ++ et C ++ 03. Cependant, il se trouve que le changement dont je parle n'a pas fait dans la norme encore, donc 03 C ++ est pas vraiment différent de 98 C ++ à cet égard (grâce à Johannes pour le souligner). D'une certaine façon, je suis sûr que je l'ai vu en C ++ 03, mais en réalité, il est pas là. Pourtant, la question existe en effet (voir la référence DR dans le commentaire Johannes) et certains compilateurs mettent déjà en oeuvre ce qu'ils considèrent probablement la solution la plus raisonnable de cette question. Ainsi, les références à 03 C ++ dans le texte ci-dessous ne sont pas correctes. S'il vous plaît, interpréter les références à 03 C ++ comme des références à certaines spécifications du futur hypothétique, mais très probable de ce comportement, que certains compilateurs tentent déjà de mettre en œuvre.


Il est important de noter qu'il y avait un changement significatif dans les droits d'accès pour les classes imbriquées entre 98 C ++ et C ++ 03 normes.

Dans 98 classe imbriquée C avait pas de droits d'accès spéciaux aux membres de classe de fermeture. Il était essentiellement classe complètement indépendant, vient de déclarer dans le cadre de la classe fermée. Il pourrait seulement l'accès publics membres de la classe englobante.

Dans 03 classe imbriquée C a été donné de droits d'accès aux membres de la classe englobante en tant que membre de la classe englobante. Plus précisément, classe imbriquée a donné les mêmes droits d'accès en fonction de membre statique de la classe englobante. C'est à dire. maintenant la classe imbriquée peut accéder tous les membres de la classe englobante, y compris privé les.

Pour cette raison, vous pouvez observer les différences entre les différents compilateurs et versions du même compilateur selon le moment où ils ont mis en œuvre la nouvelle spécification.

Bien sûr, vous devez vous rappeler qu'un objet de la classe imbriquée est pas lié en aucune façon à un objet spécifique de la classe englobante. En ce qui concerne les objets réels sont concernés, ce sont deux classes indépendantes. Pour accéder aux membres de données non statiques ou des méthodes de la classe englobante de la classe imbriquée, vous devez avoir un objet spécifique de la classe englobante. En d'autres termes, une fois encore, la classe imbriquée se comporte en effet comme comme une fonction membre statique de la classe englobante: il n'a pas le pointeur de this spécifique pour la classe englobante, donc il ne peut pas accéder à la non membres -static de la classe englobante, sauf si vous faites un effort pour donner un objet spécifique de la classe englobante d'accès. Sans elle la classe imbriquée ne peut accéder à typedef noms, énumérations et les membres statiques de la classe englobante.

Un exemple simple qui illustre la différence entre C ++ 98 et 03 C ++ peut se présenter comme suit

class E {
  enum Foo { A };
public:
  enum Bar { B };

  class I {
    Foo i; // OK in C++03, error in C++98
    Bar j; // OK in C++03, OK in C++98
  };
};

Ce changement est exactement ce qui permet à votre fonction PrivDerived::Nested::fred de compiler. Il ne passerait pas la compilation dans un compilateur C ++ 98 pédant.

Autres conseils

Réponse courte: Les classes imbriquées ont accès au député des classes contenant en C ++ 0x, mais pas 1998 C ++ et C ++ 2003. Il est cependant juridique pour 98 C ++ et C ++ 2003 compilateurs prennent en charge le comportement C ++ 0x, puisque l'ancien comportement est considéré comme un défaut.

Dans la section 98 et 2003 C ++ normes 11.8.1 a déclaré:

  

Les membres d'une classe imbriquée ont pas   un accès spécial aux membres d'un   classe de fermeture, ni aux classes ou   fonctions qui ont accordé l'amitié   à une classe externe; l'habituel   règles d'accès (article 11) sont   obéi. Les membres d'une enceinte   classe n'a pas accès spécial   membres d'une classe imbriquée; l'habituel   règles d'accès (article 11) sont   obéi.

C ++ 0x section 11.8.1 dit:

  

Une classe imbriquée est membre et en tant que tel   a les mêmes droits d'accès que tout   autre membre. Les membres d'une   classe englobante n'a pas accès spécial   aux membres d'une classe imbriquée; le   règles d'accès habituelles (article 11) Shall   être obéi.

Langue de base Rapport 45 Defect montre que le comportement d'origine était considéré comme un défaut, il est légal pour les compilateurs non c ++ 0x pour soutenir également le nouveau comportement, mais pas nécessaire.

Selon la norme:

  

9.2 Les membres du groupe

     

1 [...] Les membres d'une classe sont membres de données, les fonctions membres (9.3), les types imbriqués,   et les agents recenseurs. membres de données et les fonctions membres sont statiques ou non statique; voir les types 9.4.Nested sont   classes (9.1, 9.7) et énumérations (7.2) définies dans la classe, et les types arbitraires déclarés comme membres par l'utilisation   d'une déclaration typedef (7.1.3).

Pour répondre à vos questions:

  
      
  1. Est-ce comportement standard? (Une explication décente pourquoi serait   très utile)
  2.   

Non. Au moins les typedefs ne pas être accessibles. Cependant, notez que:

class Nested {
    // Not allowed since Nested has no access to PubDerived member data
     void foo() {std::cout << pub << "\n";}

est problématique. La classe imbriquée n'a ni une instance de PubDerived à travailler avec ni l'pub un objet membre de static.

  
      
  1. Peut-on espérer qu'il fonctionne sur la plupart des compilateurs modernes (à savoir, comment   portable est-il)?
  2.   

Oui. Mais faire vérifier la documentation de conformité aux normes. Et toujours:. Essayez avec quelques compilateurs tels que Comeau en mode strict

Je l'ai fait de mon mieux pour rassembler toutes les dispositions pertinentes de la norme ISO / CEI 14882:. 1997

Section 9.7:

  

Une classe définie dans une autre est appelée une classe imbriquée. Le nom d'une classe imbriquée est locale à sa classe englobante. La classe imbriquée est dans le cadre de sa classe englobante. Sauf en utilisant des pointeurs explicites, des références et des noms d'objet, les déclarations dans une classe imbriquée peut utiliser uniquement noms de type , les membres statiques et les agents recenseurs de la classe englobante.

11.2.1 (devrait être assez évident):

  

[...] Si une classe est déclarée être une classe de base pour une autre classe en utilisant le spécificateur d'accès privé, les membres du public et la protection de la classe de base sont accessibles en tant que membres privés de la classe dérivée.

9.9 emboîtées noms de type:

  

Les noms de type obéissent exactement les mêmes règles de portée que d'autres noms.

Puis, en 11.8:

  

Les membres d'une classe imbriquée ont pas accès spécial aux membres d'une classe englobante, ni aux classes ou fonctions qui ont accordé l'amitié à une classe englobante; les règles d'accès habituelles (11) sont respectées. Les membres d'une classe englobante ont pas accès spécial aux membres d'une classe imbriquée; les règles d'accès habituelles (11) sont respectées.

D'où je conclus que le comportement que vous rencontrez est non standard. La classe imbriquée ne devrait pas avoir accès « spécial » à l'initiative parlementaire de la base.

Cependant, Comeau C ++, qui semble avoir le meilleur support standard, a le même comportement que GCC (permet fred, n'autorise pas bar avec "Erreur: tapez "Base :: priv_t"(déclarée à la ligne 4) est inaccessible") .

Cela ne répond pas à votre question, mais d'après ma lecture de C ++ FAQ Lite 24.6 ce que vous essayez de faire est pas autorisé. Je ne sais pas pourquoi gcc est ce qui permet; avez-vous essayé dans d'autres compilateurs ainsi?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top