模板类型推导如何使用重载函数作为参数

时间:2017-12-31 14:48:52

标签: c++ c++17

看看这个(简化的)例子:

int foo(int) { return 0;}
double foo(double) { return 0.0; }

template <class T>
enable_if<is_integral<T>::value>
bar(T(*f)(T)) {}

int main()
{
   bar(foo);
   return 0;
}

我的期望是编译器将首先尝试为每个重载实例化模板,这将导致第二次重载失败(SFINAE),因此候选集合中只剩下void bar(int(*f)(int)使用foo的第一个重载来解决。这不是发生的事情。它失败了这样的事情:

no matching function for call to ‘bar(<unresolved overloaded function type>)’
couldn't deduce template parameter ‘T’

有没有办法实现这样的目标?

1 个答案:

答案 0 :(得分:2)

C ++的类型演绎非常简单。子句[temp.deduct.call]/6.2[over.over]/1描述了重载名称可用作参数的方式。

在您的示例中,两个推论都会成功(到T=intT=double)并且一个替换会失败。但这种语言只需要一次扣除即可。

你问过如何实现它。以下是一些选项:

  1. 不要将重载名称,static_cast用于所需的函数类型,也不要在调用bar时明确提供T类型。
  2. 更改重载,以便仅对其中一个重载进行演绎。换句话说,通过扣除而不是替换来排除重载。请参阅下面的示例。
  3. 添加另一个可以推断T的参数。
  4. 推卸责任,避免推迟T直到稍后。
  5. #2的例子:

    tempate<class T> struct integral_wrapper { T t; }
    
    integral_wrapper<int> foo(int);
    double foo(double);
    
    template<class T>
    void bar(integral_wrapper<T> foo(T));
    

    #3的例子:

    template<class T>
    void bar(T (*f)(T), T);
    
    bar(foo, 0);
    

    #4的例子:

    struct foo_t
    {
        int operator()(int);
        double operator()(double);
    } foo;
    
    template<class F>
    void bar(F);
    
    bar(foo);
    

    请注意,通用lambda可能是#4的另一种方法。

    根据您的使用情况,其中一些可能比其他更具吸引力。方法#4在将参数传递给STL算法时特别有用。