O +
na expressão +[](){}
é o +
operador unário . É definido da seguinte forma em [expr.unary.op] / 7:
O operando do +
operador unário deve ter aritmética, enumeração sem escopo ou tipo de ponteiro e o resultado é o valor do argumento.
O lambda não é do tipo aritmético etc., mas pode ser convertido:
[expr.prim.lambda] / 3
O tipo da expressão lambda [...] é um tipo de classe sem união sem nome único - chamado de tipo de fechamento - cujas propriedades são descritas abaixo.
[expr.prim.lambda] / 6
O tipo de fecho para um lambda-expressão sem lambda-captura tem um public
não- virtual
não-explicit
const
função de conversão para ponteiro para a função que tem os mesmos tipos de parâmetros e de retorno como operador chamada de função do tipo de fecho. O valor retornado por esta função de conversão deve obrigatoriamente ser o endereço de uma função que, quando invocada, tem o mesmo efeito que invocar o operador de chamada de função do tipo de encerramento.
Portanto, o unário +
força a conversão para o tipo de ponteiro de função, que é para este lambdavoid (*)()
. Portanto, o tipo da expressão +[](){}
é esse tipo de ponteiro de função void (*)()
.
A segunda sobrecarga void foo(void (*f)())
se torna uma correspondência exata na classificação para resolução de sobrecarga e, portanto, é escolhida sem ambigüidade (pois a primeira sobrecarga NÃO é uma correspondência exata).
O lambda [](){}
pode ser convertido por std::function<void()>
meio do modelo não explícito ctor de std::function
, que leva qualquer tipo que cumpra oCallable
CopyConstructible
requisitos e .
O lambda também pode ser convertido por void (*)()
meio da função de conversão do tipo de fechamento (veja acima).
Ambos são sequências de conversão definidas pelo usuário e da mesma classificação. É por isso que a resolução de sobrecarga falha no primeiro exemplo devido à ambigüidade.
De acordo com Cassio Neri, apoiado por um argumento de Daniel Krügler, este unário +
truque deve ser um comportamento especificado, ou seja, você pode confiar nele (veja a discussão nos comentários).
Ainda assim, eu recomendo usar uma conversão explícita para o tipo de ponteiro de função se você quiser evitar a ambigüidade: você não precisa perguntar no SO o que faz e por que funciona;)
std::bind
de umstd::function
objeto que pode ser chamado de maneira semelhante a uma função lvalue.