函数类型参数的模板参数推导

Vla*_*cow 11 c++ templates language-lawyer template-argument-deduction c++17

考虑以下程序。

#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 );
}
Run Code Online (Sandbox Code Playgroud)

程序编译成功,输出为

g( 42 );
Run Code Online (Sandbox Code Playgroud)

现在让我们将非模板函数重命名gf.

#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 );
}
Run Code Online (Sandbox Code Playgroud)

现在该程序不是由 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 )
      |         ~~~~^
Run Code Online (Sandbox Code Playgroud)

那么问题来了:代码要不要编译,gcc和clang没有编译的原因是什么?

Mic*_*zel 7

在我看来 gcc 和 clang 是正确的。这不应该编译。当T提供的参数是包含函数模板[temp.deduct.type]/5.5的重载集时,您希望从中推断出的函数参数在此处成为非推断上下文:

非推导的上下文是:

  • […]
  • 由于关联的函数参数是一个函数或一组重载函数 ([over.over]),因此无法对其进行参数推导的函数参数,并且以下一项或多项适用:

    • […]
    • 作为参数提供的一组函数包含一个或多个函数模板。
  • […]

因此,T无法推导出,并且由于没有转换,其他重载不可行;正是 gcc 所说的......