考虑以下程序。
#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 );
}
该程序编译成功,其输出为
g( 42 );
现在,将非模板函数重命名g
为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 );
}
现在该程序不是由gcc HEAD 10.0.0 20200和clang HEAD 10.0.0编译的,而是由Visual C ++ 2019成功编译的。
例如,编译器gcc发出以下消息集。
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 )
| ~~~~^
因此出现一个问题:应否编译代码,以及gcc和clang不编译代码的原因是什么?
亲自观看Godbolt
—
Zereges
注意:在第一个示例中,将
—
Xeverous
g
(而不是&g
)传递给函数模板会导致类型衰减(函数左值引用会衰减到指向函数的指针:void(&)(T)
=> void(*)(T)
)。之所以发生这种隐式转换,是因为没有其他f
具有更好匹配的重载。在第二个示例中,f
您实际上要调用一个歧义,因为...它也不知道哪个f
是参数。