Abzug von Vorlagenargumenten für ein Argument eines Funktionstyps

10

Betrachten Sie das folgende Programm.

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

Das Programm wird erfolgreich kompiliert und seine Ausgabe ist

g( 42 );

Benennen wir nun die Nicht-Vorlagenfunktion gin um 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 );
}

Jetzt wird das Programm nicht von gcc HEAD 10.0.0 20200 und clang HEAD 10.0.0 kompiliert, sondern erfolgreich von Visual C ++ 2019 kompiliert.

Beispielsweise gibt der Compiler gcc die folgenden Nachrichten aus.

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

Es stellt sich also die Frage: Sollte der Code kompiliert werden und was ist der Grund, warum der Code nicht von gcc und clang kompiliert wird?

Vlad aus Moskau
quelle
Überzeugen
Sie sich
Hinweis: Im ersten Beispiel führt die Übergabe g(anstelle von &g) an die Funktionsvorlage zu einem Typabfall (eine Funktionswertreferenz zerfällt in einen Zeiger auf eine Funktion: void(&)(T)=> void(*)(T)). Diese implizite Konvertierung erfolgt, weil es keine andere fÜberlastung mit einer besseren Übereinstimmung gibt. Im zweiten Beispiel gibt es eine Mehrdeutigkeit, die fSie tatsächlich aufrufen möchten, weil ... es auch nicht weiß, welches fdas Argument ist.
Xeverous

Antworten:

7

Es scheint mir, dass gcc und clang richtig sind. Dies sollte nicht kompiliert werden. Der Funktionsparameter, aus dem Sie Tabgeleitet werden möchten, wird hier zu einem nicht abgeleiteten Kontext, sobald das angegebene Argument eine Überladungsmenge ist, die eine Funktionsvorlage [temp.deduct.type] /5.5 enthält :

Die nicht abgeleiteten Kontexte sind:

  • […]
  • Ein Funktionsparameter, für den keine Argumentableitung durchgeführt werden kann, da das zugehörige Funktionsargument eine Funktion oder eine Reihe überladener Funktionen ([over.over]) ist und eine oder mehrere der folgenden Bedingungen zutreffen:

    • […]
    • Der als Argument bereitgestellte Funktionssatz enthält eine oder mehrere Funktionsvorlagen.
  • […]

Somit Tkann nicht abgeleitet werden , und die andere Überlastung ist nicht lebensfähig aufgrund keine Umwandlung ist; genau das, was gcc sagt ...

Michael Kenzel
quelle
0

Dies sind zwei überladene Funktionen, und die Nicht-Vorlagenfunktion sollte im Vergleich zur Vorlagenfunktion ausgewählt werden. Daher wurde f (int x) ausgewählt, sodass die Übergabe einer Funktion als Argument in der Funktion, die int übergeben werden muss, unmöglich ist. und das unten sollte funktionieren. Vielen Dank

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

  int main(){

     f( f );
  }
Lexshard
quelle
Nicht-Vorlagenfunktionen werden nur bevorzugt, wenn während der Überlastungsauflösung ansonsten ein Gleichstand besteht. Der Code der Frage kommt nicht an diesen Punkt: Es wird nie eine Spezialisierung void f<int>(void(int))generiert, um eine Überlastungsauflösung auf beiden Ebenen durchzuführen.
Davis Herring