El +
en la expresión +[](){}
es el +
operador unario . Se define de la siguiente manera en [expr.unary.op] / 7:
El operando del +
operador unario debe tener enumeración aritmética, sin ámbito o tipo puntero y el resultado es el valor del argumento.
La lambda no es de tipo aritmético, etc., pero se puede convertir:
[expr.prim.lambda] / 3
El tipo de expresión lambda [...] es un tipo de clase sin unión, sin nombre, único, llamado tipo de cierre , cuyas propiedades se describen a continuación.
[expr.prim.lambda] / 6
El tipo de cierre para un lambda-expresión sin lambda de captura tiene un public
no- virtual
no- explicit
const
función de conversión a puntero a la función que tienen los mismos parámetros y valores de retorno como operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que la invocación del operador de llamada de función del tipo de cierre.
Por lo tanto, el unario +
fuerza la conversión al tipo de puntero de función, que es para esta lambda void (*)()
. Por lo tanto, el tipo de expresión +[](){}
es este tipo de puntero de función void (*)()
.
La segunda sobrecarga se void foo(void (*f)())
convierte en una coincidencia exacta en la clasificación de resolución de sobrecarga y, por lo tanto, se elige sin ambigüedades (ya que la primera sobrecarga NO es una coincidencia exacta).
La lambda [](){}
se puede convertir a std::function<void()>
través de la plantilla no explícita ctor de std::function
, que toma cualquier tipo que cumpla con los requisitos Callable
y CopyConstructible
.
La lambda también se puede convertir a void (*)()
través de la función de conversión del tipo de cierre (ver arriba).
Ambas son secuencias de conversión definidas por el usuario y del mismo rango. Es por eso que la resolución de sobrecarga falla en el primer ejemplo debido a la ambigüedad.
Según Cassio Neri, respaldado por un argumento de Daniel Krügler, este +
truco unario debe ser un comportamiento específico, es decir, puede confiar en él (ver discusión en los comentarios).
Aún así, recomendaría usar una conversión explícita al tipo de puntero de función si desea evitar la ambigüedad: no necesita preguntarle a SO qué hace y por qué funciona;)
std::bind
de unstd::function
objeto que se puede llamar de manera similar a una función lvalue.