Déduction d'argument de modèle pour un argument d'un type de fonction

10

Considérez le programme suivant.

#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 );
}

Le programme se compile avec succès et sa sortie est

g( 42 );

Renommons maintenant la fonction non modèle gen 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 );
}

Maintenant, le programme n'est pas compilé par gcc HEAD 10.0.0 20200 et clang HEAD 10.0.0 mais compilé avec succès par Visual C ++ 2019 ..

Par exemple, le compilateur gcc émet l'ensemble de messages suivant.

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 )
      |         ~~~~^

Une question se pose donc: faut-il compiler le code et quelle est la raison pour laquelle le code n'est pas compilé par gcc et clang?

Vlad de Moscou
la source
Remarque: dans le premier exemple, le passage g(au lieu de &g) au modèle de fonction provoque une décroissance de type (une fonction lvalue référence décroît vers un pointeur vers une fonction: void(&)(T)=>void(*)(T) ). Cette conversion implicite se produit car il n'y a pas d'autre fsurcharge avec une meilleure correspondance. Dans le deuxième exemple, il y a une ambiguïté que fvous voulez réellement appeler parce que ... il ne sait pas non plus quel fest l'argument.
Xeverous

Réponses:

7

Il me semble que gcc et clang sont corrects. Cela ne devrait pas être compilé. Le paramètre de fonction à partir duquel vous souhaitez Têtre déduit devient un contexte non déduit ici au moment où l'argument fourni est un ensemble de surcharge qui contient un modèle de fonction [temp.deduct.type] /5.5 :

Les contextes non déduits sont:

  • […]
  • Un paramètre de fonction pour lequel la déduction d'argument ne peut pas être effectuée car l'argument de fonction associé est une fonction ou un ensemble de fonctions surchargées ([over.over]), et une ou plusieurs des conditions suivantes s'appliquent:

    • […]
    • l'ensemble des fonctions fournies en argument contient un ou plusieurs modèles de fonctions.
  • […]

Ainsi, Tne peut être déduit et l'autre surcharge n'est pas viable en raison de l'absence de conversion; exactement ce que dit gcc…

Michael Kenzel
la source
0

Ce sont deux fonctions surchargées et la fonction non modèle doit être sélectionnée par rapport à la fonction de modèle, donc f (int x) a été sélectionné, il est donc impossible de passer une fonction comme argument dans la fonction qui doit être transmise. et ce qui suit devrait fonctionner. Merci

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

  int main(){

     f( f );
  }
Lexshard
la source
Les fonctions qui ne sont pas des modèles sont préférées uniquement lorsqu'il y a sinon égalité lors de la résolution de surcharge. Le code de la question ne parvient pas à ce point: il n'y a jamais de spécialisation void f<int>(void(int))générée pour effectuer une résolution de surcharge à l'un ou l'autre niveau.
Davis Herring