类型演绎不适用于std::function

Type deduction does not work with std::function

本文关键字:function std 适用于 演绎 不适用 类型      更新时间:2023-10-16

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

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的类型?

提前感谢。

类型演绎在您的情况下不起作用,因为它不能被演绎。在大多数情况下,类型推导是与类型和其他模板参数的简单匹配。然而,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,而vector版本则很容易匹配。

你会对我说:"但是……指向函数的指针可以转换为std::函数,你这个傻瓜!"是的,它的确是敞篷的。但是在任何转换之前,编译器必须查找T是什么。没有T,从指针到函数到什么类的转换?很多课吗?尝试匹配每个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的东西,可以与任何可调用的工作,你可以想象!

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

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

在本例中,您将func限制为可调用int,并且仍然返回void

这里的问题是编译器必须做重载解析,以找出哪些std::function<U(U)>实例化具有可以接受T(*)(T)的构造函数。也就是说,可能有多种类型,每种类型都可能有多个可以获取input_func的因子。

现在,如果你看一下标准,你会发现没有为std::function指定这样的重载,但是重载解析规则对所有模板都是一样的,不管它们是来自std::boost::还是ACME::。编译器不会开始实例化模板来查找转换序列。

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

在这个特殊的例子中,你知道函数指针和std::function之间的关系,也知道特定的限制,你必须有一个返回相同类型的统一函数(没有T(*)(U)),这样你就可以添加一个重载

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

将function转换为std::函数显式地解决了这个问题。例如,下面的代码可以工作:

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;
}