Considere o seguinte 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 );
}
O programa é compilado com sucesso e sua saída é
g( 42 );
Agora vamos renomear a função não modelo g
para 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 );
}
Agora, o programa não é compilado pelo gcc HEAD 10.0.0 20200 e clang HEAD 10.0.0, mas compilado com sucesso pelo Visual C ++ 2019 ..
Por exemplo, o compilador gcc emite o seguinte conjunto de mensagens.
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 )
| ~~~~^
Portanto, surge uma pergunta: o código deve ser compilado e qual é a razão pela qual o código não é compilado pelo gcc e pelo clang?
c++
templates
language-lawyer
c++17
template-argument-deduction
Vlad de Moscou
fonte
fonte
g
(em vez de&g
) para o modelo de função causa uma deterioração de tipo (uma referência de valor da função decai para um ponteiro para uma função:void(&)(T)
=>void(*)(T)
). Essa conversão implícita acontece porque não há outraf
sobrecarga com uma correspondência melhor. No segundo exemplo, há uma ambiguidade quef
você deseja chamar de fato porque ... também não sabe qualf
é o argumento.Respostas:
Parece-me que gcc e clang estão corretos. Isso não deve compilar. O parâmetro de função do qual você deseja
T
deduzir se torna um contexto não deduzido aqui no momento em que o argumento fornecido é um conjunto de sobrecargas que contém um modelo de função [temp.deduct.type] /5.5 :Assim,
T
não pode ser deduzido e a outra sobrecarga não é viável por não haver conversão; exatamente o que o gcc diz…fonte
Essas são duas funções sobrecarregadas e a função não modelo deve ser selecionada em comparação com a função modelada; portanto, f (int x) foi selecionado, portanto, é impossível passar uma função como argumento na função na qual int deve ser passado. e o abaixo deve funcionar. obrigado
fonte
void f<int>(void(int))
gerada para executar a resolução de sobrecarga nos dois níveis.