2010-05-29 11 views
2

Peut-être encore mieux est: Pourquoi la norme nécessite-t-elle une transmission à une classe de base dans ces situations? (Ouais ouais ouais - Pourquoi - Parce que?.)Où dans la norme transmet à une classe de base requis dans ces situations?

class B1 { 
public: 
    virtual void f()=0; 
}; 
class B2 { 
public: 
    virtual void f(){} 
}; 
class D : public B1,public B2{ 
}; 
class D2 : public B1,public B2{ 
public: 
    using B2::f; 
}; 
class D3 : public B1,public B2{ 
public: 
    void f(){ 
     B2::f(); 
    } 
}; 
D d; 
D2 d2; 
D3 d3; 

MS donne:

sourceFile.cpp 
sourceFile.cpp(24) : error C2259: 'D' : cannot instantiate abstract class 
     due to following members: 
     'void B1::f(void)' : is abstract 
     sourceFile.cpp(6) : see declaration of 'B1::f' 
sourceFile.cpp(25) : error C2259: 'D2' : cannot instantiate abstract class 
     due to following members: 
     'void B1::f(void)' : is abstract 
     sourceFile.cpp(6) : see declaration of 'B 

et de même pour le compilateur MS.

I peut-être acheter le premier cas, D. Mais dans D2-f est sans ambiguïté défini par la déclaration using, pourquoi est-ce que cela ne suffit pas pour le compilateur d'être nécessaire pour remplir le vtable? Où dans la norme cette situation est-elle définie?

ajouté pour répondre à Reponse

En ce qui concerne la réponse ci-dessous que je l'ai accepté:

Pourquoi cela semble pas une erreur dans la spécification? - Si on a une hiérarchie d'héritage avec une série de f() non virtuels, dont l'utilisation dans les classes dérivées est déterminée en utilisant des instructions, et on change le decl de f dans une classe de base en virtual alors cela peut changer f est appelé dans les classes dérivées en utilisant des instructions pour choisir leur f. C'est un "gotcha" C++ dont je n'étais pas au courant. Cela peut faire partie de la langue, mais une telle «action à distance» me met mal à l'aise et me semble être une violation d'un principe d'exactitude/d'entretien (que je ne peux pas formuler tout à l'heure).

Mais je peux donner un exemple:

#include <iostream> 
using std::cout; 


namespace NonVirtual_f{ 

class C0 { 
public: 
    void f(){cout<<"C0::f()"<<'\n';} 
}; 

class C1 : public C0{ 
public: 
    void f(){cout<<"C1::f()"<<'\n';} 
}; 

class C2 : public virtual C1{ 
public: 
    void f(){cout<<"C2::f()"<<'\n';} 
}; 

class D3 : public virtual C1, public C2{ 
public: 
    using C1::f; 
}; 


}//namespace NonVirtual_f 

namespace Virtual_f{ 


class C0 { 
public: 
    virtual void f(){cout<<"C0::f()"<<'\n';} 
}; 

class C1 : public C0{ 
public: 
    void f(){cout<<"C1::f()"<<'\n';} 
}; 

class C2 : public virtual C1{ 
public: 
    void f(){cout<<"C2::f()"<<'\n';} 
}; 

class D3 : public virtual C1, public C2{ 
public: 
    using C1::f; 
}; 



}//namespace Virtual_f 




int main(int argc,const char* const*argv){ 

    NonVirtual_f::D3 nv3; 
    nv3.f(); 

    Virtual_f::D3 v3; 
    v3.f(); 

    return 0;  
} 

D'où la sortie:

C1::f() 
C2::f() 

Tout ce qui est changé est la virtualité de f en C0. En particulier lorsque la non-virtualité de f dans une classe de base est choisie, elle ne peut pas être changée sans problèmes de maintenance si une classe dérivée (en général une classe ne peut pas) a été "surchargée" comme dans l'exemple immédiatement au-dessus.

Si vous contrez avec "Eh bien, ne surchargez pas de cette façon dans le cas NonVirtual", je suis d'accord c'est une mauvaise pratique mais cela semble plus que cela.Pour moi, la langue doit:

permettent pas l'utilisation de dans nonvirtual :: D3 (pas possible actuellement car il peut y avoir d'autres faire de surcharge f à [à moins d'utiliser autorisés une signature dans le cas de la fonction])

ou

refuser l'aide d'instructions de fonctions complètement et la force d'envoi

ou

avoir l'aide passer outre en fait dans tous les cas

ou

permettent à certaines déclarative pour les fonctions syntaxiques (essentiellement une fonction à l'aide) comme:

void f(*signature*) = C2::f; 

Qu'est-ce, exactement, suis-je manque ici? Quelqu'un peut-il proposer un scénario qui clarifie le «pourquoi» de ce choix dans la norme?

+0

Pour votre information: les erreurs sont affichées à partir de Visual C++ :-) –

+0

EDG "sourceFile.cpp", ligne 24: Erreur: objet du type de classe abstraite "D" n'est pas autorisé: la fonction virtuelle pure "B1 :: f" n'a pas de priorité D d; ^ "sourceFile.cpp", ligne 25: erreur: objet de type abstrait de classe "D2" est interdite: fonction virtuelle pure "B1 :: f" n'a pas de crosse D2 d2; ^ – pgast

+0

Vous le savez peut-être déjà, mais juste en tant que FYI, si vous aviez mis f() = 0 dans une classe de base virtuelle de B1 et B2, alors D fonctionnerait. Voir la délégation de la classe soeur: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.10 –

Répondre

3

La norme C++ dit dans § 10.3/2:

The rules for member lookup (10.2) are used to determine the final overrider for a virtual function in the scope of a derived class but ignoring names introduced by using-declarations.

Donc, même si vous utilisez using B2::f; pour apporter B2::f() dans la classe dérivée, on ne considère pas passer outre B1::f().

Ainsi, D2 est abstraite en raison de §10.4/4:

A class is abstract if it contains or inherits at least one pure virtual function for which the final overrider is pure virtual.

+0

Court et précis. +1, plus si je pouvais. –

+0

Et pourquoi cela ne semble-t-il pas une erreur dans la spécification? - Si on a une hiérarchie d'héritage avec une série de f() non virtuels, dont l'utilisation dans les classes dérivées est déterminée en utilisant des instructions, et on change le decl de f dans une classe de base en virtual alors cela peut changer f est appelé dans les classes dérivées en utilisant des instructions pour choisir leur f. C'est un "gotcha" C++ dont je n'étais pas au courant. Cela peut faire partie de la langue, mais une telle «action à distance» me met mal à l'aise et me semble être une violation d'une sorte de principe de correction/maintenance (que je ne peux pas formuler tout à l'heure) – pgast