Deducción de argumento de plantilla para un argumento de un tipo de función

10

Considere el siguiente programa.

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void g( int x )
{
    std::cout << "g( " << x << " );\n";
}

int main()
{
    f( g );
}

El programa se compila correctamente y su salida es

g( 42 );

Ahora cambiemos el nombre de la función sin plantilla ga f.

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void f( int x )
{
    std::cout << "f( " << x << " );\n"; 
}

int main()
{
    f( f );
}

Ahora el programa no está compilado por gcc HEAD 10.0.0 20200 y clang HEAD 10.0.0 sino compilado con éxito por Visual C ++ 2019 ..

Por ejemplo, el compilador gcc emite el siguiente conjunto de mensajes.

prog.cc: In function 'int main()':
prog.cc:22:10: error: no matching function for call to 'f(<unresolved overloaded function type>)'
   22 |     f( f );
      |          ^
prog.cc:4:6: note: candidate: 'template<class T> void f(void (*)(T))'
    4 | void f( void ( *fn )( T ) )
      |      ^
prog.cc:4:6: note:   template argument deduction/substitution failed:
prog.cc:22:10: note:   couldn't deduce template parameter 'T'
   22 |     f( f );
      |          ^
prog.cc:14:6: note: candidate: 'void f(int)'
   14 | void f( int x )
      |      ^
prog.cc:14:13: note:   no known conversion for argument 1 from '<unresolved overloaded function type>' to 'int'
   14 | void f( int x )
      |         ~~~~^

Entonces surge una pregunta: ¿debe compilarse el código y cuál es la razón por la cual el código no está compilado por gcc y clang?

Vlad de Moscú
fuente
Nota: en el primer ejemplo, pasar g(en lugar de &g) a la plantilla de función provoca una caída de tipo (una referencia de valor de función l decae a un puntero a una función: void(&)(T)=> void(*)(T)). Esta conversión implícita ocurre porque no hay otra fsobrecarga con una mejor coincidencia. En el segundo ejemplo, hay una ambigüedad a la que fquieres llamar porque ... tampoco sabe cuál fes el argumento.
Xeverous

Respuestas:

7

Me parece que gcc y clang son correctos. Esto no debe compilarse. El parámetro de función del que desea Tdeducirse se convierte en un contexto no deducido aquí en el momento en que el argumento suministrado es un conjunto de sobrecarga que contiene una plantilla de función [temp.deduct.type] /5.5 :

Los contextos no deducidos son:

  • [...]
  • Un parámetro de función para el que no se puede realizar la deducción de argumentos porque el argumento de función asociado es una función o un conjunto de funciones sobrecargadas ([over.over]), y se aplica uno o más de los siguientes:

    • [...]
    • El conjunto de funciones suministradas como argumento contiene una o más plantillas de funciones.
  • [...]

Por lo tanto, Tno se puede deducir y la otra sobrecarga no es viable debido a que no hay conversión; exactamente lo que dice gcc ...

Michael Kenzel
fuente
0

Estas son dos funciones sobrecargadas y la función sin plantilla se debe seleccionar en comparación con la función con plantilla, por lo que se seleccionó f (int x), por lo tanto, pasar una función como argumento en la función que int debe pasar es imposible. y lo siguiente debería funcionar. Gracias

void f( void ( *fn )( int ) ){
  fn( 42 );
}
void f( int x ){
    std::cout << "f( " << x << " );\n";
  }

  int main(){

     f( f );
  }
Lexshard
fuente
Las funciones que no son de plantilla se prefieren solo cuando existe un vínculo durante la resolución de sobrecarga. El código de la pregunta no llega a ese punto: nunca se void f<int>(void(int))genera una especialización para hacer una resolución de sobrecarga en cualquier nivel.
Davis Herring