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 g
na 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?
c++
templates
language-lawyer
c++17
template-argument-deduction
Vlad z Moskwy
źródło
źródło
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 innegof
przeciążenia z lepszym dopasowaniem. W drugim przykładzie istnieje dwuznaczność, którąf
chcesz wywołać, ponieważ ... nie wie również, któryf
jest argumentem.Odpowiedzi:
Wydaje mi się, że gcc i clang mają rację. Nie powinno się to kompilować. Parametr funkcji, z którego chcesz
T
się wydedukować, staje się kontekstem niededukowanym tutaj, gdy podany argument jest zestawem przeciążenia, który zawiera szablon funkcji [temp.deduct.type] /5,5 :Zatem
T
nie można tego wywnioskować, a inne przeciążenie nie jest wykonalne z powodu braku konwersji; dokładnie to, co mówi gcc…źródło
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
źródło
void f<int>(void(int))
generowana specjalizacja , aby rozwiązać problem przeciążenia na obu poziomach.