10
votes

Quelles sont les règles de choix des fonctions de modèle surchargées?

Compte tenu du code ci-dessous, pourquoi est la fonction foo (t *) sélectionnée?

Si je le supprime (le foo (t *) ) Toujours compile et fonctionne correctement, mais g ++ v4.4.0 (et probablement d'autres compilateurs également) généreront deux fonctions FOO () Fonctions: une pour char [4] et une pour char [7]. xxx


5 commentaires

Drôle, MSVC9 Noms la fonction "FOO " Dans les symboles de débogage, mais - apparemment, car il reconnaît que la mise en œuvre est identique - utilise la même mise en œuvre pour les deux appels. - Question intéressante, de toute façon.


Afaik VC se plie des instances de modèle qui entraînent un code identique.


GCC (4.2.4) fait à la fois FOO et FOO Instanciations dans quelque chose jusqu'à -O3, où il les élit tout simplement pour ces implémentations et le rend en ligne.


Nicki explicitement interdit à VC de les aligner


@peterchen: Évidemment, VC se plie toujours des instances identiques.


4 Réponses :


-1
votes

cause "" est un char *, qui convient parfaitement à la fonction FOO (T *). Lorsque vous supprimez ceci, le compilateur essaiera de le faire fonctionner avec FOO (T &), ce qui nécessite que vous transmettez la référence à la matrice de caractère contenant la chaîne.

Compilateur ne peut pas générer une seule fonction qui recevrait une référence à caractère, car vous passez une matrice entière, elle doit donc la désirer.


2 commentaires

Mal aussi. S'il vous plaît ne devinez pas. "" est un charret [1] . Vous ne pouvez pas non plus référence de transmission . Les expressions ont toujours un type de non-référence.


Je n'ai jamais dit que ce n'est pas (en fait, c'est "", Co Const Const [2]), ce que je voulais dire, c'est que choisir la fonction T * est préférable, si elle existe. En ce qui concerne la déclaration Réf , j'essayais de dire que l'option moins préférée est T &, qui est fondamentalement, une référence à T (dans ce cas, référence au char [n]), bien que mon La réponse n'a pas semblé assez correcte, à votre droite avec celle-là. Corrigez-moi si je me trompe (et après que vous le ferai, je vais simplement supprimer mon message, car ce n'est pas utile par rapport à la vôtre, ce qui est beaucoup plus complet).



6
votes

Formellement, lorsque la comparaison des séquences de conversion, les transformations de LValue sont ignorées. Les conversions sont regroupées dans plusieurs catégories, comme Réglage de qualification ( T * -> t const * ), transformation de lvalue ( int [n] -> int * , vide () -> void (*) () ) , et d'autres.

La seule différence entre vos deux candidats est une transformation de lvalue. Les littéraux de chaîne sont des matrices qui se convertissent en pointeurs. Le premier candidat accepte la matrice par référence et n'aura donc pas besoin d'une transformation de lvalue. Le deuxième candidat nécessite une transformation de lvalue.

Donc, s'il existe deux candidats que les deux spécialisations de modèles de fonctionnement sont également viables en recherchant uniquement les conversions, la règle est que plus spécialisée est choisie par la commande partielle des deux.

comparer les deux en regardons leur signature de leur liste de paramètres de fonction xxx

si nous choisissons un type unique q q Pour la première liste de paramètres et essayer de correspondre à la deuxième liste de paramètres, nous correspondons à q contre t * . Cela échouera, car q n'est pas un pointeur. Ainsi, la seconde est au moins aussi spécialisée que la première.

Si nous faisons l'inverse, nous correspondons à q * contre t const & . La référence est abandonnée et les qualificateurs de toplevel sont ignorés et le reste t devient q * . Il s'agit d'une correspondance exacte aux fins de la commande partielle et donc de déduction de la liste de paramètres transformée de la seconde contre le premier candidat aboutit. Étant donné que l'autre direction (contre la seconde) n'a pas réussi, le deuxième candidat est plus spécialisé que le premier - et en conséquence, la résolution de surcharge préférera la seconde, si elle aurait autrement une ambiguïté.

au 13.3.3.2/3 :

Séquence de conversion standard S1 est une meilleure séquence de conversion que la séquence de conversion standard S2 si [...]

  • s1 est une approche appropriée de S2 (comparant les séquences de conversion de la forme canonique défini par 13.3.3.1.1, excluant toute transformation de lvalue; La séquence de conversion d'identité est considérée comme une subséquente de toute séquence de conversion non-identité) ou, sinon que [...]

    alors 13.3.3 / 1

    • Soit ICSI (F) désignant la séquence de conversion implicite qui convertit le I-ème argument de la liste au type du paramètre I-T ème de la fonction viable F. 13.3.3.1 Définit les séquences de conversion implicites et 13.3.3.2 définit ce que cela signifie pour une séquence de conversion implicite pour être une meilleure séquence de conversion ou une séquence de conversion pire qu'un autre.

      Compte tenu de ces définitions, une fonction viable F1 est définie comme une meilleure fonction qu'une autre fonction viable F2 si pour tous les arguments I, ICSI (F1) n'est pas une séquence de conversion pire que l'ICSI (F2), puis .]

      • F1 et F2 sont des spécialisations de gabarit de fonction et le modèle de fonction pour F1 est plus spécialisé que le modèle de F2 en fonction des règles de commande partielle décrites dans 14.5.5.2 ou, sinon cela, [...]

        Enfin, voici la table des conversions implicites pouvant participer à une séquence de conversion standard à 13.3.3.1.1 / 3 .

        séquences de conversion http://img259.imageshack.us/img259/851/convs.png


7 commentaires

Ce n'est pas un cas de préférence sans modèles, car il n'y a pas de fonction non-modèle à choisir par le compilateur. Le fait est que Const Char [] est une correspondance beaucoup plus proche de T * que T & (parce que Const Char [] et Const Char * sont équivalents).


Désolé j'ai négligé le modèle <...> clause avant le second. Correction :)


Votre assertion que la correspondance à t * est plus proche est fausse, cependant :)


@LitB: Si mon assertion était fausse, le compilateur ne serait pas très bien d'accord avec moi. const t [n]> const t *> const t &, dans ce cas. Bien sûr, il ne fournit pas une surcharge de const T [n]. Const T * gagne (Eh bien, dans le cas de l'OP, T *, qui est encore plus faible, mais toujours préférable au compilateur que Const T &).


@LITB: Je pense que nous ne sommes tout simplement pas d'accord sur la définition de "plus près" ... Je veux simplement dire "plus proche" d'être "plus préférable" aux yeux de la norme (et donc de compilateur).


J'allais ajouter un commentaire différent, mais je me rendais compte que mon commentaire précédent WSA OK! :( Quoi qu'il en soit, juste pour le réorganiser, si vous ajoutez une superposition avec Rederence to TRAY: "Modèle VOID FOO (T (&) [I])" Alors, cela se traduit par une erreur d'appel ambiguë , puisque la commande partielle ne peut pas distinguer les spécialisations.


@Richard, sûr, votre commentaire précédent était bien :) (Peut-être que je vais améliorer ma réponse, mais pas maintenant je suis trop fatigué en ce moment). :)



0
votes

10 commentaires

t * n'est en aucun cas une correspondance plus proche. Essayez void f (charret const (&) [1]); vide f (charret const *); int Main () {F (""); } : Cet appel, qui ressemble à la situation ci-dessus à l'aide de non-modèles, est ambigu.


@LITB: T * est un match beaucoup plus proche que T & dans ce cas. Oui, si vous incluez une taille spécifique (& [N]), cela le rend ambigu, mais T * correspond à une matrice arbitraire beaucoup plus étroitement que T & Le fait.


@Nick, tandis que Void Foo (T x []) est un synonyme de Void Foo (T * X), ​​cela ne signifie pas que vous ne pouvez pas instancier Void FOO (T &) avec T étant un type de matrice. L'équivalent est vide FOO (ArrayBaseType (&) [Arraysize]))


@Aprogrammer: Droite, je ne voulais pas vous suggérer de ne pas pouvoir le faire (en fait, il est clair que le compilateur ne le fera aucune autre option), mais simplement que cela soit moins préféré.


@Nick, veuillez indiquer les sections standard appropriées au lieu de faire de plus en plus de réclamations. Votre analyse semble être légèrement libre pour moi, franchement parler.


@LitB: 4.2 (2) Spécifie la conversion littérale à chaîne (utilisée dans ce cas car nous n'avons aucune surcharge explicite d'une taille de matrice donnée) est considérée comme une matrice-to-pointeur suivie de la conversion de qualification (à des fins de classement dans la résolution de la surcharge ). T * et T qualifient comme "correspondance exacte" aux fins de chaque conversion dans la séquence, mais T & dispose d'un rang de "conversion" pour la liaison de référence inférieure à 13.3.3.1.4, ce qui signifie que T * est un meilleur match de 13,3.3 .3.1.1.3, comme il a une "correspondance exacte" pour la liaison de référence. Au moins, c'est comme ça que je le lis.


@Nick, "123" Reliure à un t const & Lorsque t est un paramètre de modèle, puis la séquence de conversion est la conversion d'identité (ainsi, elle ne contient aucune conversion): car Le t const & se lie directement à l'argument "123" (13.3.3.1.4 / 1) (avec t devient char [ 4] ). Même si nous devions accepter t & , alors t deviendra charret [4] , et ce sera une correspondance exacte aussi: le résultat résultant Le paramètre de fonction est de type Char Cons (&) [4] . La conversion de qualification que vous avez mentionnée n'a pas à voir avec cela, mais avec char * c = "123"; . Mais dans nos exemples, il apparaît nulle part un type char * .


Notez qu'il existe une "référence compatible avec la qualification ajoutée". Cela se produit par exemple avec void f (int const &); int a; int Main () {F (a)} . Mais ce sera toujours une correspondance exacte (conversion d'identité). Néanmoins, cela peut influer sur le rang de la séquence de conversion, comme indiqué par Void F (int Const &); vide f (int &); int a; int main () {f (a); } Il appellera le second, car il est moins qualifié. Mais cela ne fera aucune conversion. Lors de la déduction de l'argument contre t * , (argument est toujours "123" avec type Char Cons [4] ), une transformation de lvalue .. .


(Tableau à la conversion du pointeur) se produit par le processus de déduction, pour obtenir le type Char const * (puis a = chartons * et p = t * En entrant 14.8.2.4 ). Le compilateur déduira ensuite chartons pour T, et va donc créer un paramètre de fonction de type chartons * . Maintenant, "123" nécessite une transformation de lvalue pour l'appel pour réussir (tableau au pointeur), mais aucune conversion de qualification ou quelle que soit la conversion de qualification. Donc, en fait, le t * a une conversion plus. Cette conversion de point-pointeur est toutefois ignorée lors de la comparaison de deux séquences de conversion. Et cela, l'appel serait ambigu ...


Maintenant, le compilateur voit que les deux candidats sont des fonctions instanciées à partir de modèles et rechercheront leurs modèles, déterminant une commande partielle. Ensuite, le candidat correspondant au modèle plus spécialisé (s'il existe une commande entre eux) sera choisi par résolution de la surcharge. Et c'est dans notre cas la version t * . J'espère que mon analyse dans les commentaires clarifie la question.



2
votes

La réponse complète est assez technique.

Premier, les littéraux de chaîne ont charret [n] type.

Il y a une conversion implicite de const [n] à Char Const * .

Donc, la fonction de la fonction de votre modèle, une liaison de référence, l'une utilisant la conversion implicite. Lorsqu'ils sont seuls, les deux fonctions de votre modèle sont capables de gérer les appels, mais quand ils sont tous les deux présents, nous devons expliquer pourquoi la seconde foo (instanciée avec T = Char Cons [N]) est une meilleure correspondance que la première ( instancié avec T = CHAR). Si vous regardez les règles de surcharge (comme indiqué par LitB), le choix entre xxx

et xxx

est ambiguë (Les règles sont assez compliquées, mais vous pouvez vérifier en écrivant des fonctions de non-modèle avec de telles signatures et voyez que le compilateur se plaint). Dans ce cas, le choix est fait au deuxième parce que celui-ci est plus spécialisé (à nouveau que les règles de ce commandant partiel sont compliquées, mais dans ce cas, c'est parce que vous pouvez passer un charret [n] à un Char Cons * mais pas un Char Const * à un Char Cons [N] de la même manière que vide Bar (Char Cons *) est plus spécialisé que Barre de void (Char *) Parce que vous pouvez passer un char * à un Char Const * < / code> mais pas vise-versa).


0 commentaires