2013-04-06 1 views
20

J'ai ce codeLe compilateur pense que "A (A &)" accepte les valeurs pour un moment?

struct A { A(); A(A&); }; 
struct B { B(const A&); }; 

void f(A); 
void f(B); 

int main() { 
    f(A()); 
} 

À ma grande surprise cela échoue avec GCC et Clang. Clang dit par exemple

Compilation finished with errors: 
source.cpp:8:10: error: no matching constructor for initialization of 'A' 
     f(A()); 
     ^~~ 
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument 
    struct A { A(); A(A&); }; 
        ^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided 
    struct A { A(); A(A&); }; 
      ^
source.cpp:4:13: note: passing argument to parameter here 
    void f(A); 

Pourquoi choisissent-ils le premier f, lorsque le second f fonctionne bien? Si je supprime le premier f, l'appel réussit. Ce qui est plus étrange pour moi, si j'utilise l'initialisation du corset, cela fonctionne aussi bien

int main() { 
    f({A()}); 
} 

Ils appellent la deuxième f.

Répondre

17

C'est une bizarrerie linguistique. La première f correspond mieux car votre A ne nécessite aucune conversion pour correspondre au type d'argument (A), mais lorsque le compilateur tente d'effectuer l'appel, le fait qu'aucun constructeur de copie approprié ne puisse être trouvé provoque l'échec de l'appel. Le langage ne permet pas de prendre en compte la faisabilité de l'appel réel lors de l'exécution de l'étape de résolution de surcharge.

le plus proche correspondant norme citation ISO/IEC 14882: 2011 13.3.3.1.2 séquences de conversion définis par l'utilisateur [over.ics.user]:

Une conversion d'une expression du type de classe à la même classe type reçoit le rang Exact Match et une conversion d'une expression de type classe à une classe de base de ce type reçoit un rang de conversion, malgré le fait que un constructeur copy/move (c'est-à-dire une fonction de conversion définie par l'utilisateur)) est appelé pour ces cas.

Pour le cas d'initialisation de la liste, vous avez probablement besoin de regarder: 13.3.3.1.2 séquences de conversion définies par l'utilisateur [over.ics.user]

Lorsque des objets de classe non globale type T sont la liste initialisées (8.5.4), la résolution de surcharge sélectionne le constructeur en deux phases:

- dans un premier temps, les fonctions candidats sont les constructeurs initialiseur-liste (8.5.4) de la classe T et la liste d'arguments se compose de la liste d'initialisation comme un sing l'argument.

- Si aucun constructeur viable liste initialiseur se trouve, la résolution de surcharge est effectuée à nouveau, où les fonctions candidats sont tous les constructeurs de la classe T et la liste des arguments est constitué des éléments de la liste des initialiseur.

Parce que la résolution de surcharge doit examiner contructeurs viables dans chaque cas pour f(A) et f(B) il doit rejeter la seqence essayer de lier A() à A(A&) mais B(const A&) est encore viable.

+0

Merci! Je ne peux pas trouver une telle règle pour le cas {...} '. Est-ce que cela explique pourquoi le cas '{...}' fonctionne? –

+0

@ JohannesSchaub-litb: Je ne suis pas sûr, tbh, vous appelez la fonction avec un _braced-init-list_ donc les règles sont définitivement différentes. –

+0

@ JohannesSchaub-litb Voir [over.ics.list]. Je pense que cela a à voir avec [over.ics.ref]/3 (J'ai mal interprété votre code plus tôt): Lors de la formation du sous-ensemble de fonctions viables, le ctor 'A (A &)' n'est pas considéré comme viable car il lie temporairement une référence de lvalue non-const. – dyp

Questions connexes