Considere lo siguiente que usa el operador ternario para obtener el tipo de puntero de función común de las dos lambdas
int main() { true ? [](auto) noexcept {} : [](int) {}; }GCC-trunk solo lo acepta en C++14 pero lo rechaza en C++17/20 con ( Demo ):
<source>:2:8: error: operands to '?:' have different types 'main()::<lambda(auto:1)>' and 'main()::<lambda(int)>' 2 | true ? [](auto) noexcept {} : [](int) {}; | ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Clang-trunk lo acepta en todos los modos C++14/17/20 ( Demo ).
MSVC-trunk solo lo acepta en C++20 pero lo rechaza en C++14/17 con ( Demo ):
<source>(2): error C2446: ':': no conversion from 'main::<lambda_01e5bb79b5a210014fb78333f6af80f9>' to 'main::<lambda_57cf6f5767bc1bee4c1e1d9859a585d2>' <source>(2): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called¿Qué compilador es el correcto?
Dado que cada expresión lambda tiene un tipo único y ninguna expresión lambda se puede convertir en otra, se aplica [expr.cond]/6 .
Si el segundo y el tercer operando no tienen el mismo tipo, y cualquiera tiene un tipo de clase (posiblemente cv calificado), la resolución de sobrecarga se usa para determinar las conversiones (si las hay) que se aplicarán a los operandos ([over.match.oper ], [sobreconstruido]). Si la resolución de sobrecarga falla, el programa está mal formado. De lo contrario, se aplican las conversiones así determinadas y los operandos convertidos se usan en lugar de los operandos originales para el resto de esta subcláusula.
Los candidatos son ( [over.built]/25 ):
Para cada tipo
T, dondeTes un puntero, puntero a miembro o tipo de enumeración con ámbito, existen funciones de operador candidatas de la formaT operator?:(bool, T, T);
Creo que la resolución de sobrecarga debería tener éxito en este caso y, como resultado, ambos operandos deberían convertirse en void(*)(int) .
Es decir, IMO clang es correcto.
Editar: creo que [](auto) noexcept {} es convertible a void(*)(int) , porque tiene una plantilla de función de conversión template<class T> operator FP() , donde FP es void(*)(T) ( [expr.prim.lambda.cierre]/9 ).
Para una lambda genérica sin captura de lambda, el tipo de cierre tiene una plantilla de función de conversión para apuntar a la función. La plantilla de función de conversión tiene la misma lista de parámetros de plantilla inventada, y el puntero a función tiene los mismos tipos de parámetros que la plantilla de operador de llamada de función. El tipo de retorno del puntero a la función se comportará como si fuera un especificador de tipo de declinación que denota el tipo de retorno de la especialización de plantilla de operador de llamada de función correspondiente.
Y creo que la deducción del argumento de la plantilla debería hacer que esta plantilla de conversión se pueda utilizar como una conversión a void(*)(int) ( [temp.deduct.conv]/5 ).
La deducción del argumento de la plantilla se realiza comparando el tipo de retorno de la plantilla de la función de conversión (llámela
P) con el tipo especificado por el id del tipo de conversión de la id de la función de conversión que se busca (llámelaA) como se describe en [ temp.deduct.type].[...]
En general, el proceso de deducción intenta encontrar valores de argumentos de plantilla que hagan que la
Adeducida sea idéntica aA. Sin embargo, ciertos atributos deApueden ignorarse:
- [...]
- Si el
Aoriginal es un puntero de función o un tipo de función de puntero a miembro, esnoexcept.