Odliczenie argumentu szablonu dla argumentu typu funkcji

10

Rozważ następujący program.

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

Program kompiluje się pomyślnie, a jego wynik to

g( 42 );

Teraz zmieńmy nazwę funkcji innej niż szablon gna 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 );
}

Teraz program nie jest kompilowany przez gcc HEAD 10.0.0 20200 i clang HEAD 10.0.0, ale pomyślnie skompilowany przez Visual C ++ 2019 ..

Na przykład gcc kompilatora wydaje następujący zestaw komunikatów.

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

Powstaje więc pytanie: czy kod powinien zostać skompilowany i jaki jest powód, dla którego kod nie jest skompilowany przez gcc i clang?

Vlad z Moskwy
źródło
Uwaga: w pierwszym przykładzie przekazanie g(zamiast &g) szablonu funkcji powoduje zanik typu (odwołanie do wartości funkcji zanika do wskaźnika do funkcji: void(&)(T)=> void(*)(T)). Ta niejawna konwersja ma miejsce, ponieważ nie ma innego fprzeciążenia z lepszym dopasowaniem. W drugim przykładzie istnieje dwuznaczność, którą fchcesz wywołać, ponieważ ... nie wie również, który fjest argumentem.
Xeverous

Odpowiedzi:

7

Wydaje mi się, że gcc i clang mają rację. Nie powinno się to kompilować. Parametr funkcji, z którego chcesz Tsię wydedukować, staje się kontekstem niededukowanym tutaj, gdy podany argument jest zestawem przeciążenia, który zawiera szablon funkcji [temp.deduct.type] /5,5 :

Nie wywnioskowano kontekstów:

  • […]
  • Parametr funkcji, dla którego nie można wykonać odliczenia argumentu, ponieważ powiązany argument funkcji jest funkcją lub zestawem funkcji przeciążonych ([over.over]) i zastosowanie ma co najmniej jedna z następujących czynności:

    • […]
    • zestaw funkcji dostarczonych jako argument zawiera jeden lub więcej szablonów funkcji.
  • […]

Zatem Tnie można tego wywnioskować, a inne przeciążenie nie jest wykonalne z powodu braku konwersji; dokładnie to, co mówi gcc…

Michael Kenzel
źródło
0

Są to dwie przeciążone funkcje i należy wybrać funkcję nie-szablonową w porównaniu do funkcji szablonowanej, więc f (int x) zostało wybrane, dlatego przekazanie funkcji jako argumentu w funkcji, którą int musi zostać przekazane, jest niemożliwe. i poniżej powinny działać. Dzięki

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

  int main(){

     f( f );
  }
Lexshard
źródło
Funkcje inne niż szablony są preferowane tylko wtedy, gdy w innym przypadku występuje remis podczas rozwiązywania przeciążenia. Kod pytania nie dochodzi do tego punktu: nigdy nie jest void f<int>(void(int))generowana specjalizacja , aby rozwiązać problem przeciążenia na obu poziomach.
Davis Herring