类型推导不适用于 std::function

Type deduction does not work with std::function

我有以下问题。当我尝试编译以下代码时

template< typename T >
T func( T t)
{
  return t;
}

template< size_t N, typename T >
void foo( std::function< T(T) > func )
{
  // ...
}

int main()
{
  foo<3>( func<float> );

  return 0;
}

我收到错误:

 no matching function for call to 'foo'
      foo<3>( func<float> );
      ^~~~~~
/Users/arirasch/WWU/dev/xcode/tests/tests/main.cpp:18:10: note: candidate template ignored: could not match 'function<type-parameter-0-1 (type-parameter-0-1)>' against 'float (*)(float)'
    void foo( std::function< T(T) > func )

然而,当我将其修复为

template< typename T >
T func( T t)
{
  return t;
}

template< size_t N, typename T >
void foo( std::function< T(T) > func )
{
  // ...
}

int main()
{

  std::function< float(float) > input_func = func<float>;
  foo<3>( input_func );

  return 0;
}

即当我将foo的输入函数显式声明为std::function< float(float) >时,可以编译成功

有谁知道我可以如何替代地修复我的代码,这样我就可以简单地编写类似 foo<3>( func<float> ); 的东西(根据我的第一个代码示例)而不是

std::function< float(float) > input_func = func<float>;
foo<3>( input_func );

在哪里必须明确说明 input_func 的类型?

非常感谢。

这里的问题是编译器必须进行重载解析以找出哪些 std::function<U(U)> 实例具有可以采用 T(*)(T) 的构造函数。也就是说,可能有多种类型,每种类型可能有多个 ctors 可以占用您的 input_func.

现在,如果您查看标准,您会发现没有为 std::function 指定此类重载,但所有模板的重载解析规则都是相同的,无论它们来自 std::boost::ACME::。编译器不会开始实例化模板来查找转换序列。

一旦您提供了完美匹配,就不需要转换序列了。只有一种目标类型不需要转换序列,并且编译器会推导出这种类型。

在这个特殊情况下,你知道函数指针和std::function之间的关系,也知道你必须有一个returns相同类型(没有T(*)(U)) 所以你可以添加一个重载

template< size_t N, typename T >
void foo(T(*func)(T))
{
  return foo<N>(std::function<T(T)>(func));
}

将 func 转换为 std::function 明确解决了问题。例如。下面的代码有效。

template< typename T >
T func( T t)
{
  return t;
}

template<typename T>
using FuncType = std::function<T(T)>;

template< size_t N, typename T >
void foo( FuncType<T> func )
{
  // ...
}

int main()
{
  func<float>(1.0);
  foo<3>( FuncType<float>(func<float>) );

  return 0;
}

类型推导在您的情况下不起作用,仅仅是因为它无法推导。在大多数情况下,类型推导是与类型和其他模板参数的简单匹配。然而,C++ 的一些黑暗角落处理具有一些时髦规则的推论,但我不会在这个答案中深入探讨。

这是编译器可以推导出模板参数的示例:

template<typename T>
void test(std::vector<T>);

test(std::vector<int>{1, 2, 3, 4, 5, 6});

这对编译器来说很容易。它需要 Tstd::vector。你给它 intstd::vectorT 必须是 int.

但是,在您的情况下,发生了很多事情:

template<typename T>
void test(std::function<T(T)>);

int someFunc(int);

test(someFunc);

编译器无法匹配。自己尝试一下:给我一个 T 可以使这两个类型相等:int(*)(int)std::function<T(T)>。事实上,T 不可能使这两种类型相同,而矢量版本很容易匹配。

你会对我说:"but... a pointer to a function is convertible to a std::function you silly!" 是的,它确实是可转换的。但在任何转换之前,编译器 必须 找出 T 是什么。如果没有 T,您将函数指针转换为什么 class?许多class?尝试匹配每个 T?您的函数可以转换的可能性有多种。


你怎样才能完成这项工作?忘记 std::function。刚收到T.

template<typename T>
T func(T t) {
  return t;
}

template<size_t N, typename T>
void foo(T func) {
  // ...
}

int main()
{
  foo<3>( func<float> );

  return 0;
}

注意这个例子是如何运作的。你没有转换,没有 std::function 东西,可以使用你能想象到的任何可调用对象!

您是否担心接受任何类型?不用担心!无论如何,参数类型是表达模板可以对接收到的参数做什么的糟糕方式。您应该使用 expression 来限制它。该表达式将告诉其他人您将如何使用 T 以及 T 需要具有的接口。顺便说一句,我们称之为 sfinae:

template<size_t N, typename T>
auto foo(T func) -> decltype(void(func(std::declval<int>()))) {
  // ...
}

在此示例中,您将 func 限制为可以使用 int 调用并且仍然 return void.