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.operator +()
a um tipo de fechamento sem estado. Suponha que esse operador retorne algo diferente do ponteiro para a função para a qual o tipo de fechamento se converte. Então, isso alteraria o comportamento observável de um programa que viola 5.1.2 / 3. Por favor, deixe-me saber se você concorda com este raciocínio.operator +
, mas isso se compara à situação em que não há nenhumoperator +
para começar. Mas não é especificado que o tipo de fechamento não deve ter umaoperator +
sobrecarga. "Uma implementação pode definir o tipo de fechamento de maneira diferente do que é descrito abaixo, desde que isso não altere o comportamento observável do programa, exceto por [...]" mas a adição de um operador IMO não altera o tipo de fechamento para algo diferente do que é "descrito abaixo".operator +()
é exatamente a descrita pelo padrão. O padrão permite que uma implementação faça algo diferente do que está especificado. Por exemplo, adicionaroperator +()
. No entanto, se essa diferença for observada por um programa, ela é ilegal. Certa vez, perguntei em comp.lang.c ++. Moderated se um tipo de encerramento poderia adicionar um typedef pararesult_type
e outrotypedefs
necessário para torná-los adaptáveis (por exemplo, porstd::not1
). Disseram-me que não, porque isso era observável. Vou tentar encontrar o link.