2010-02-03 2 views
2

je le code suivant C++ dans Visual Studio 2005 ...C++: Pourquoi VS2005 interprète-t-il une initialisation directe de l'instance locale comme une fonction, lorsque le constructeur de la classe a un paramètre polymorphe?

class Base {}; 
class Derived : public Base {}; 

class Other { 
public: 
Other(const Base& obj) {} 
void test() {} 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
Other other(Derived()); 
other.test(); 
return 0; 
} 

... Compilation échoue et donne:

test.cpp(19) : error C2228: left of '.test' must have class/struct/union 

J'ai déterminé par quelques tests que cela se produit parce que le La déclaration de la variable "other" est interprétée comme une déclaration de fonction (renvoyant un Autre et prenant un paramètre Derived), au lieu d'une instance de Other utilisant le constructeur à un seul argument. (VS6 trouve le constructeur et compile très bien, mais ce n'est pas bon à la norme C++, donc je ne lui fais pas confiance par rapport à VS2005)

Si je fais ...

Other other(static_cast<Base&>(Derived())); 

... ou de l'utilisation copie-initialisation, ça fonctionne bien. Mais il ne semble pas voir que l'instance Derived() est dérivée de Base par elle-même, ou elle donne la priorité à la déclaration de fonction au lieu d'essayer le polymorphisme sur le paramètre constructeur.

Ma question est: est ce comportement C++ standard, ou est ce comportement VS2005-spécifique? Au cas où ...

Other other(Derived()); 

... déclarer une instance locale dans la norme C++, ou doit-il déclarer une fonction?

+0

pour votre information, cette question est appelée « Le plus contrariant Parse » –

+0

Il vous manque des points-virgules dans votre classe 'Other' mais ça ne fait pas partie de votre question ... –

Répondre

3

Oui, ceci est un comportement standard. Voir this C++ FAQ-lite entry.

Selon la norme, ceci:

Other other(Derived()); 

est interprété comme une déclaration de fonction, d'une fonction qui retourne Autre et prend comme paramètre une autre fonction qui renvoie dérivée et n'a pas de paramètres. Pour résoudre ce problème, vous pouvez utiliser:

Other other = Other(Derived()); 
+0

Wow, merci. Ironiquement, j'avais déjà lu ce lien dans le passé, mais je dois l'avoir oublié (probablement parce que la raison donnée - l'interprétation des paramètres - est assez obscure). Je supposais que cela avait à voir avec le polymorphisme car le static_cast (...) fonctionnait aussi. – DustOff

1

J'ai essayé votre exemple dans VS2008, et GCC et cela arrive aussi.

Ce qu'il ressemble, est que cette syntaxe est en fait déclarer d'autres comme un pointeur de fonction avec cette déclaration:

Other other(Derived (*)(void)) 

Le comportement correct devrait être d'utiliser l'initialisation:

Other other = Derived(); 
2

Vous mentionnez "paramètre polymorphique" dans le titre de votre question, alors que tout cela n'a absolument rien à voir avec des paramètres polymorphes. En C++, la déclaration problématique déclare une fonction indépendamment du fait que l'argument que vous fournissez soit polymorphe ou non.

Comme d'habitude, vous pouvez utiliser une paire supplémentaire de () travailler autour de la question

Other other((Derived())); // now it is an object, not a function 

Vous pouvez également utiliser toute autre méthode pour faire tourner la partie Derived() dans une expression, comme

Other other((const Derived&) Derived()); 

ou

Other other(((void) 0, Derived())); 
+0

Merci, ouais j'ai réalisé après la première réponse que le truc de polymorphisme venait de mes propres hypothèses dues aux circonstances (je ne pensais pas essayer static_cast (...), ce qui aurait rendu cela évident). La parenthèse supplémentaire sera très utile. Mais, un peu hors sujet, que fait (0), Dérivé())? – DustOff

+0

@DustOff: '((void) 0, Derived())' est une expression qui contient un opérateur virgule au milieu. Le résultat de l'opérateur virgule est sa dernière sous-expression - 'Derived()' - qui est ce dont nous avons besoin. La première sous-expression - '(void) 0' - est simplement ignorée. Juste un simple '0' (ou' 1' ou '42') fonctionnera aussi, mais le compilateur émettra des avertissements sur l'expression qui" n'a aucun effet ". Une conversion en '(void)' supprime l'avertissement. La valeur de cette étrange méthode est purement académique, bien sûr :) – AnT

Questions connexes