¿Por qué el tipo de argumento &&
en el siguiente ejemplo no se comporta como la llamada "referencia universal" a pesar de que parece estar en un "contexto deducido" y, en cambio, se interpreta como un valor r:
auto main() -> int { using MyFn = void (*)(int &); MyFn special = [](auto && thing) {}; // compile error, deduced as int && rather than int& return 0; }
Mientras que lo siguiente, que espero que sea equivalente, ¿funciona?
template <typename T> void foo(T &&){}; auto main() -> int { using MyFn = void (*)(int &); MyFn specialC = foo; return 0; }
Sospecho que hay algún detalle esotérico con la conversión de lambdas en punteros de función que importa aquí, pero hasta ahora no puedo resolverlo.
Las referencias de reenvío están respaldadas por una regla de deducción especial. La regla establece que si necesitamos deducir T
para hacer que T&&
sea idéntico a U&
, entonces T
se deduce como U&
(de modo que T&&
será U& (&&)
, que simplemente se reduce a U&
).
Sin embargo, esta regla especial solo se aplica en determinadas circunstancias: al deducir argumentos de plantilla de plantilla de función basados en los argumentos de una llamada de función ( [temp.deduct.call]/3 ) (en el caso de que el argumento sea un valor l de tipo U
, que puede imaginarse con el tipo " U&
"), al tomar la dirección de una plantilla de función y al hacer coincidir una declaración de especialización de plantilla de función con una declaración de plantilla principal ( [temp.deduct.type]/10 ).
Su caso no es ninguno de los anteriores, sino que es un caso separado de deducción de argumentos de plantilla para una plantilla de función de conversión ( [temp.deduct.conv] ), que no tiene la regla especial para reenviar referencias.
Otro ejemplo en el que no se aplican las reglas de deducción de referencia de reenvío es el siguiente, que por lo tanto no compilará:
template <class T> void foo(void(*)(T&&)) {} void bar(int&) {} int main() { foo(bar); }
En los parámetros lambda, auto
por sí solo da una deducción de tipo universal. Así que esto hace lo que quieres:
MyFn special = [](auto thing) {};
Demostración usando un tipo no copiable para demostrar que no está pasando por valor: https://godbolt.org/z/5vfWc6c8x