111 votes

Résolution de la surcharge ambiguë sur le pointeur de fonction et std :: function pour un lambda à l'aide de +

Dans le code suivant, le premier appel à foo est ambigu et par conséquent, la compilation échoue.

La seconde, avec les + ajoutés avant le lambda, résout la surcharge du pointeur de la fonction.

 #include <functional>

void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }

int main ()
{
    foo(  [](){} ); // ambiguous
    foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}
 

Que fait la notation + ici?

115voto

dyp Points 19641

L' + dans l'expression +[](){} est unaire + de l'opérateur. Il est défini comme suit dans [expr.unaire.op]/7:

L'opérande de l'unaire + opérateur aura l'arithmétique, non délimité énumération, ou de type pointeur et le résultat est la valeur de l'argument.

Le lambda n'est pas de l'arithmétique type etc., mais il peut être converti:

[expr.prim.lambda]/3

Le type de la lambda-expression [...] est un unique, sans nom non-union type de classe qu'on appelle la fermeture type - dont les propriétés sont décrites ci-dessous.

[expr.prim.lambda]/6

La fermeture pour un type lambda-expression avec pas de lambda-capture a un public nonvirtual nonexplicit const fonction de conversion de pointeur de fonction ayant le même paramètre et les types de retour de la fermeture du type de la fonction d'appel de l'opérateur. La valeur retournée par cette fonction de conversion doit être l'adresse d'une fonction qui, lorsqu'il est appelé, a le même effet que l'invocation de la fermeture du type de la fonction d'appel de l'opérateur.

Par conséquent, le unaire + des forces de la conversion à la fonction de type pointeur, qui est pour cette lambda void (*)(). Par conséquent, le type de l'expression +[](){} est-ce la fonction de type de pointeur void (*)().

La seconde surcharge void foo(void (*f)()) devient une Correspondance Exacte dans le classement pour la résolution de surcharge et est donc choisis sans ambiguïté (comme la première surcharge n'est PAS une Correspondance Exacte).


Le lambda [](){} peut être converti std::function<void()> via le non-explicite modèle ctor d' std::function, qui prend n'importe quel type qui remplit l' Callable et CopyConstructible exigences.

Le lambda peut également être converti en void (*)() via la fonction de conversion de la fermeture type (voir ci-dessus).

Les deux sont définis par l'utilisateur de conversion de séquences, et de même rang. C'est pourquoi la résolution de surcharge d'échec dans le premier exemple en raison de l'ambiguïté.


Selon Cassio Neri, soutenue par un argument par Daniel Krügler, ce unaire + astuce doit être spécifiée comportement, c'est à dire que vous pouvez compter sur elle (voir la discussion dans les commentaires).

Encore, je vous recommande d'utiliser un cast explicite pour la fonction type de pointeur, si vous voulez éviter l'ambiguïté: vous n'avez pas besoin de demander SI ce qui est fait et pourquoi il fonctionne ;)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X