S'ils sont liés
Supposons pour un moment que B
est en fait une base de D
. Puis pour l'appel à check
les deux versions sont viables car Host
peut être converti en D*
y B*
. Il s'agit d'une séquence de conversion définie par l'utilisateur et décrite par 13.3.3.1.2
de Host<B, D>
a D*
y B*
respectivement. Pour trouver les fonctions de conversion qui peuvent convertir la classe, les fonctions candidates suivantes sont synthétisées pour la première check
selon la fonction 13.3.1.5/1
D* (Host<B, D>&)
La première fonction de conversion n'est pas candidate, car B*
ne peut pas être converti en D*
.
Pour la deuxième fonction, les candidats suivants existent :
B* (Host<B, D> const&)
D* (Host<B, D>&)
Ce sont les deux fonctions de conversion candidates qui prennent l'objet hôte. La première le prend par référence constante, et la seconde ne le prend pas. Ainsi, la seconde correspond mieux à l'objet non-const. *this
(l'objet argument implicite de l'objet ) par 13.3.3.2/3b1sb4
et est utilisé pour convertir en B*
pour le deuxième check
fonction.
Si vous voulez supprimer le const, nous aurions les candidats suivants
B* (Host<B, D>&)
D* (Host<B, D>&)
Cela signifierait que nous ne pouvons plus sélectionner par constance. Dans un scénario ordinaire de résolution de surcharge, l'appel serait maintenant ambigu parce que normalement le type de retour ne participe pas à la résolution de surcharge. Pour les fonctions de conversion, cependant, il existe une porte dérobée. Si deux fonctions de conversion sont aussi bonnes l'une que l'autre, alors leur type de retour décide qui est le meilleur selon 13.3.3/1
. Ainsi, si vous enlevez le const, alors le premier sera pris, car B*
se convertit mieux en B*
que D*
a B*
.
Maintenant, quelle séquence de conversion définie par l'utilisateur est la meilleure ? Celle de la deuxième ou de la première fonction de contrôle ? La règle est que les séquences de conversion définies par l'utilisateur ne peuvent être comparées que si elles utilisent la même fonction de conversion ou le même constructeur, conformément à la norme 13.3.3.2/3b2
. C'est exactement le cas ici : Les deux utilisent la deuxième fonction de conversion. Remarquez qu'ainsi le const est important car il oblige le compilateur à prendre la deuxième fonction de conversion.
Puisque nous pouvons les comparer, lequel est le meilleur ? La règle est que la meilleure conversion du type de retour de la fonction de conversion au type de destination l'emporte (encore une fois par 13.3.3.2/3b2
). Dans ce cas, D*
se convertit mieux en D*
que de B*
. Ainsi la première fonction est sélectionnée et nous reconnaissons l'héritage !
Remarquez que puisque nous n'avons jamais eu besoin de en fait convertir en une classe de base, nous pouvons ainsi reconnaître héritage privé parce que si nous pouvons convertir un D*
a un B*
ne dépend pas de la forme de l'héritage selon 4.10/3
S'ils ne sont pas liés
Supposons maintenant qu'ils ne sont pas liés par héritage. Ainsi, pour la première fonction, nous avons les candidats suivants
D* (Host<B, D>&)
Et pour le second, nous avons maintenant une autre série
B* (Host<B, D> const&)
Puisque nous ne pouvons pas convertir D*
a B*
si nous n'avons pas de relation d'héritage, nous n'avons maintenant aucune fonction de conversion commune parmi les deux séquences de conversion définies par l'utilisateur ! Ainsi, nous serions ambiguë si ce n'est que la première fonction est un modèle. Les modèles sont un second choix lorsqu'il existe une fonction non-modèle qui est tout aussi bonne selon les critères suivants 13.3.3/1
. Ainsi, nous sélectionnons la fonction non modèle (la deuxième) et nous reconnaissons qu'il n'y a pas d'héritage entre B
y D
!
4 votes
C'est très confus de votre part d'utiliser le même identifiant pour un paramètre de modèle et un vrai nom de classe...
1 votes
@Matthieu M., j'ai pris sur moi de corriger :)
0 votes
Voulez-vous dire dans 3 : Pourquoi
static yes check<int>(D*, T=int);
est meilleur questatic no check(B*, int);
?0 votes
@MSalters. Non. Pourquoi la fonction de modèle est meilleure que celle sans modèle. Et pourquoi est-il important que
operator B*()
est constant ?2 votes
Il y a quelque temps, j'ai écrit une implémentation alternative de
is_base_of
: ideone.com/T0C1V Il ne fonctionne pas avec les anciennes versions de GCC (GCC4.3 fonctionne bien).0 votes
@Alexey : vous avez demandé
static yes check(B*, int);
mais le code n'a qu'unstatic no check(B*, int);
.0 votes
@litb, votre solution alternative sur ideone.com ne fonctionne pas sur VC2008 et VC2010.
0 votes
@Kirill c'est probablement parce qu'il permet de lier des références non-const aux rvalues. Si vous désactivez les extensions, alors peut-être que cela fonctionne là. Dans tous les cas, il devrait avertir sur les niveaux d'avertissement élevés.
3 votes
Ok, je vais aller faire un tour.
1 votes
@JohannesSchaub-litb : le lien vers ideone.com/T0C1V donne une page blanche
0 votes
@MatthieuM.Des choses comme ça sont la raison pour laquelle je toujours préfixe
template
arguments avecT_
;-)0 votes
Dans gcc is_base_of est dérivé de __is_base_of. Je ne trouve nulle part la définition de __is_base_of. Quelqu'un sait-il dans quel en-tête __is_base_of est défini ?
2 votes
Cette mise en œuvre n'est pas correcte.
is_base_of<Base,Base>::value
devrait êtretrue
ce qui renvoiefalse
.1 votes
En plus d'un calcul incorrect
is_base_of<A,A>
cette implémentation calcule également de manière incorrecteis_base_of<void,A>
!0 votes
@QuentinUK :
__is_base_of(T,U)
est un compilateur intégré, c'est-à-dire magique. Il ne fait pas partie du langage C++ ; il est intrinsèque à GCC. La seule raison de son existence (AFAIK) est d'accélérer l'évaluation de ce trait de type, qui (si vous n'utilisez pas le builtin magique) nécessite beaucoup d'instanciation de template et de résolution de surcharge. Il y a beaucoup d'autres builtins magiques de ce type, tels que__is_union
,__is_trivially_constructible
y__is_final
.0 votes
@chengiz : cette implémentation peut-elle être corrigée pour corriger ce bug ?