18

Dans le code suivant, il semble que la classe C n'a pas accès au constructeur de A, ce qui est nécessaire en raison de l'héritage virtuel. Pourtant, le code compile et fonctionne encore. Pourquoi ça marche?Problème d'héritage virtuel privé C++

class A {}; 
class B: private virtual A {}; 
class C: public B {}; 

int main() { 
    C c; 
    return 0; 
} 

De plus, si je supprime le constructeur par défaut de A, par ex.

class A { 
public: 
    A(int) {} 
}; 
class B: private virtual A { 
public: 
    B() : A(3) {} 
}; 

puis

class C: public B {}; 

aurait (de façon inattendue) la compilation, mais

class C: public B { 
public: 
    C() {} 
}; 

ne compilerait pas, comme prévu.

Code compilé avec "g ++ (GCC) 3.4.4 (spécial cygming, gdc 0.12, en utilisant dmd 0.125)", mais il a été vérifié qu'il se comporte de la même manière avec d'autres compilateurs.

+0

Avec g ++ 4.4, il compile. Bien que je n'ai pas été en mesure de trouver une référence faisant autorité, je crois qu'il devrait compiler. La classe la plus dérivée 'C' peut construire le sous-objet de type' A'. Notez qu'il existe des implémentations pour sceller l'héritage sur la base de la combinaison de l'héritage 'private virtual '* ensemble * avec un constructeur privé dans' A' et de l'accès accordé '' '' par amitié. Toute la complication serait inutile si juste utiliser l'héritage virtuel privé suffirait. –

+0

@ DavidRodríguez-dribeas "_Toute la complication serait inutile s'il suffisait d'utiliser l'héritage virtuel privé." Personne ici n'a prétendu que l'idiome de cachetage fonctionne sans un cteur privé. Dans l'idiome de cachetage, l'héritage privé n'est pas nécessaire, mais il est nécessaire pour que l'utilisation de l'idiome soit un détail d'implémentation. – curiousguy

Répondre

13

Selon la classe C++ Core Issue #7, il est impossible de dériver une classe avec une base privée virtuelle. Ceci est un bug dans le compilateur.

+0

Excepté que g ++ et como ne se plaignent pas de l'exemple du problème principal. Cela suffit à me faire douter de ma réponse, mais je voudrais une référence plus récente que l'une des anciennes questions qui pourraient très bien ne pas avoir été mises à jour si les règles ont changé. – AProgrammer

+0

En fait, ce problème a été résolu, ce qui signifie que ce n'est pas un problème. –

+0

"_class avec une base privée virtuelle ne peut être dérivé de" Wrong. – curiousguy

2

Les classes de base virtuelles sont toujours initialisées à partir des classes les plus dérivées (C ici). Le compilateur doit vérifier que le constructeur est accessible (ie j'obtiens une erreur avec g ++ 3.4 pour

class A { public: A(int) {} }; 
class B: private virtual A {public: B() : A(0) {} }; 
class C: public B {}; 

int main() { 
    C c; 
    return 0; 
} 

alors que votre description implique qu'il n'y en a pas), mais le fait que comme base, A est privée ou non n » t importe (subvertir serait facile: class C: public B, private virtual A).

La raison pour laquelle les constructeurs de classes de base virtuelles sont appelés à partir de la classe la plus dérivée est qu'il doit être construit avant que les classes les aient comme classe de base. Edit: Kirill a mentionné un vieux problème de base qui est en désaccord avec ma lecture et le comportement des compilateurs récents. Je vais essayer d'obtenir des références standard d'une manière ou d'une autre, mais cela peut prendre du temps.

+0

Vous avez raison bien sûr. – curiousguy

+0

Vous devriez dire que vous abordez seulement la 2ème question et non la 1ère. – ndkrempel

6

Pour la deuxième question, c'est probablement parce que vous ne le faites pas implicitement définir . Si le constructeur est simplement déclaré implicitement, il n'y a pas d'erreur. Exemple:

struct A { A(int); }; 
struct B : A { }; 
// goes fine up to here 

// not anymore: default constructor now is implicitly defined 
// (because it's used) 
B b; 

Pour votre première question, cela dépend du nom utilisé par le compilateur. Je ne sais pas ce que la norme précise, mais ce code par exemple est correct parce que le nom de la classe externe (au lieu du nom de la classe héritée) est accessible:

class A {}; 
class B: private virtual A {}; 
class C: public B { C(): ::A() { } }; // don't use B::A 

Peut-être que la norme est underspecified à ce stade. Nous devrons regarder.


Il ne semble pas y avoir de problème avec le code. De plus, il y a une indication que le code est valide. Le sous-objet de classe de base (virtuel) est initialisé par défaut - il n'y a pas de texte qui implique que la recherche de nom pour le nom de la classe est diner dans la portée de C.Voici ce que dit Standard:

12.6.2/8 (C++ 0x)

Si un membre de données non statique, ou une classe de base ne sont pas nommés par un mem-initialiseur-id (y compris le cas où il n'y a pas mem-initialiseur liste parce que le constructeur n'a pas cteur-initialiseur) et l'entité n'est pas une classe de base virtuelle d'une classe abstraite

[...] sinon, l'entité est par défaut-initialisés

Et C++ 03 a un texte similaire (texte moins clair - il dit simplement que son constructeur par défaut est appelé à un endroit, et à un autre il le rend dépendant si la classe est un POD). Pour que le compilateur initialise le sous-objet par défaut, il doit simplement appeler son constructeur par défaut - il n'est pas nécessaire de chercher le nom de la classe de base en premier (sait déjà quelle base est considérée).

Considérez ce code qui est certainement destiné à être valable, mais cela échouera si ce serait être fait (voir 12.6.2/4 en C++ 0x)

struct A { }; 
struct B : virtual A { }; 
struct C : B, A { }; 
C c; 

Si le constructeur par défaut du compilateur simplement regarder -up nom de la classe A à l'intérieur de C, il aurait un résultat de recherche ambiguë en ce qui concerne ce sous-objet à initialiser, parce que les A non-virtuel et les noms de classe A virtuel sont trouvés. Si votre code est destiné à être mal formé, je dirais que la norme doit certainement être clarifiée.


Pour le constructeur, notez ce que 12.4/6 dit au sujet de la destructor de C:

Tous les Destructeurs sont appelés comme si elles étaient référencées avec un nom qualifié, qui est, sans tenir compte des Destructeurs prépondérants virtuels possibles dans plus de classes dérivées.

Cela peut être interprété de deux façons:

  • appeler A :: ~ A()
  • appeler :: A :: ~ A()

Il semble moi que la norme est moins claire ici. La deuxième façon de le rendre valide (par 3.4.3/6, C++ 0x, parce que les deux noms de classe A sont recherchés dans la portée globale), tandis que le premier le rendra invalide (parce que les deux A trouveront les noms de classe hérités). Cela dépend aussi de quel sous-objet la recherche commence (et je crois que nous devrons utiliser le sous-objet de la classe de base virtuelle comme point de départ). Si cela va comme

virtual_base -> A::~A(); 

Ensuite, nous allons trouver directement la base virtuelle » nom de classe comme un nom public, parce que nous ne passer par la classe dérivée champs d'application et de trouver le nom non accessible. Encore une fois, le raisonnement est similaire.Tenir compte:

struct A { }; 
struct B : A { }; 
struct C : B, A { 
} c; 

Si le destructor serait simplement appeler this->A::~A(), cet appel ne serait pas valide en raison du résultat de recherche ambiguë de A comme un nom de classe héritée (vous ne pouvez pas se référer à une non-statique membre fonction du diriger l'objet de classe de base de la portée C, voir 10.1/3, C++ 03). Il devra uniquement identifier les noms de classe concernés et doit commencer par la référence de sous-objet de la classe, telle que a_subobject->::A::~A();.

+1

"_it dépend du nom utilisé par le compilateur._" ??? Aucun nom n'est "utilisé" et il n'y a pas de problème de recherche de nom. ** Les constructeurs sont appelés. ** Ils feraient mieux d'être accessibles. – curiousguy

+0

@curieux votre commentaire n'a pas de sens pour moi. Demandez-vous ce que «cela dépend du nom utilisé par le compilateur»? Pourquoi faites-vous "Les constructeurs sont appelés." audacieux? FWIW, la vérification d'accès est faite sur les noms, pas sur autre chose. Il est également fait sur con/destructeurs mais c'est quelque chose qui n'est pas bien décrit dans la norme. Vous n'avez pas lu ma réponse complète, il semble. Il cite "Tous les destructeurs sont appelés comme s'ils étaient référencés avec un nom qualifié" - "aucun problème de recherche de nom"? Drôle. Vous devez être plus clair sur vos préoccupations ... –

+0

"_FWIW, la vérification d'accès se fait sur les noms, _" Non. La vérification de l'accès se fait sur déclaration utilisée. La déclaration n'est pas accessible ici. "_Il est également fait sur con/destructeurs, mais c'est quelque chose qui n'est pas bien décrit dans la norme." "Beaucoup de choses vont sans dire. "_It quotes" Tous les destructeurs sont appelés comme s'ils étaient référencés avec un nom qualifié "-" aucun problème de recherche de nom "? _" Quel problème de recherche de nom? – curiousguy