Risoluzione del sovraccarico ambiguo sul puntatore di funzione e std :: function per un lambda che utilizza +

Nel seguente codice, la prima chiamata a foo è ambigua e quindi non riesce a compilare.

Il secondo, con l’aggiunta + prima del lambda, risolve il sovraccarico del puntatore di funzione.

 #include  void foo(std::function f) { f(); } void foo(void (*f)()) { f(); } int main () { foo( [](){} ); // ambiguous foo( +[](){} ); // not ambiguous (calls the function pointer overload) } 

Cosa fa la notazione + qui?

Il + nell’espressione +[](){} è l’operatore unario + . È definito come segue in [expr.unary.op] / 7:

L’operando dell’operatore unario + deve avere l’enumerazione aritmetica, senza ambito o il tipo di puntatore e il risultato è il valore dell’argomento.

Il lambda non è di tipo aritmetico ecc., Ma può essere convertito:

[Expr.prim.lambda] / 3

Il tipo di espressione lambda […] è un tipo di class non unione univoco, senza nome, chiamato tipo di chiusura , le cui proprietà sono descritte di seguito.

[Expr.prim.lambda] / 6

Il tipo di chiusura per un’espressione lambda senza cattura lambda ha una funzione di conversione const non virtual non explicit per puntare a una funzione con lo stesso parametro e tipi di ritorno dell’operatore di chiamata di funzione del tipo di chiusura. Il valore restituito da questa funzione di conversione deve essere l’indirizzo di una funzione che, invocata, ha lo stesso effetto del richiamo dell’operatore di chiamata di funzione del tipo di chiusura.

Pertanto, unario + impone la conversione al tipo di puntatore della funzione, che è per questo lambda void (*)() . Pertanto, il tipo dell’espressione +[](){} è questo tipo di puntatore a funzione void (*)() .

Il secondo overload void foo(void (*f)()) diventa una corrispondenza esatta nella classifica per la risoluzione del sovraccarico ed è quindi scelto in modo non ambiguo (poiché il primo sovraccarico NON è un match esatto).


Il lambda [](){} può essere convertito in std::function tramite il template ctor non esplicito di std::function , che accetta qualsiasi tipo che soddisfi i requisiti Callable e CopyConstructible .

Il lambda può anche essere convertito in void (*)() tramite la funzione di conversione del tipo di chiusura (vedi sopra).

Entrambe sono sequenze di conversione definite dall’utente e dello stesso rango. Ecco perché la risoluzione del sovraccarico non riesce nel primo esempio a causa dell’ambiguità.


Secondo Cassio Neri, supportato da una discussione di Daniel Krügler, questo trucco unario + dovrebbe essere un comportamento specificato, cioè si può fare affidamento su di esso (vedere la discussione nei commenti).

Tuttavia, ti consiglio di utilizzare un cast esplicito per il tipo di puntatore a funzione se vuoi evitare l’ambiguità: non devi chiedere su SO cosa fa e perché funziona;)