带参数包的功能对象出现“模板参数推导/替换失败”错误

时间:2019-05-05 13:21:20

标签: c++ templates variadic-templates std-function template-deduction

我正在尝试创建一个函数,该函数接受可变数量的任何类型的参数,但是即使我所做的简单示例也遇到了错误

#include <iostream>
#include <functional>

template<class... Ts>
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
{
    function(parameters...);
}

void myFunc(const std::string& output)
{
    std::cout << output << std::endl;
}

int main() 
{
    callFunction<const std::string&>(&myFunc, "Hello world");
    return 0;
}

当我在Ideone中运行以上代码时,出现此错误:

prog.cpp: In function ‘int main()’:
prog.cpp:17:57: error: no matching function for call to ‘callFunction(void (*)(const string&), const char [12])’
  callFunction<const std::string&>(&myFunc, "Hello world");
                                                         ^
prog.cpp:5:6: note: candidate: template<class ... Ts> void callFunction(const std::function<void(Ts ...)>&, Ts ...)
 void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
      ^~~~~~~~~~~~
prog.cpp:5:6: note:   template argument deduction/substitution failed:
prog.cpp:17:57: note:   mismatched types ‘const std::function<void(Ts ...)>’ and ‘void (*)(const string&) {aka void (*)(const std::__cxx11::basic_string<char>&)}’
  callFunction<const std::string&>(&myFunc, "Hello world");

1 个答案:

答案 0 :(得分:2)

一个简单的建议:以可推断的类型名而不是std::function的形式接收可调用对象

我的意思是(还添加了完美的转发功能)

template <typename F, typename ... Ts>
void callFunction(F const & func, Ts && ... pars)
 { func(std::forward<Ts>(pars)...); }

很明显,在不进行任何说明的情况下调用它

callFunction(&myFunc, "Hello world");

这是避免将可调用对象转换为std::function的额外优势。

无论如何,我在您的代码中看到两个问题:

1)如果您以std::function的形式接收函数,并且以相同类型的参数列表的形式接收参数类型列表(在这种情况下为可变列表,但对这个问题并不重要),您必须确保两个列表中的类型完全匹配

这不是您的情况,因为该函数接收到std::string const &,并且您将作为字符串"Hello world"的字符串常量char const [12]作为参数传递给参数。

要推导类型时,这会导致编译错误,因为编译器无法在两种类型之间进行选择。

您可以解决接收两个类型列表

template <typename ... Ts1, typename Ts2>
void callFunction (std::function<void(Ts1...)> const & function,
                   Ts2 && ... parameters)
 { function(std::forward<Ts2>(parameters)...); }

但是现在我们有第二个问题

2)您传递了一个指针函数(&myFunc,其中callFunction()等待一个std::function

我们遇到了鸡蛋问题,因为&myFunc可以转换为std::function但不是std::function

因此,编译器无法从Ts...推断出&myFunc类型列表,因为它不是std::function,也无法将&myFunc转换为{{1 }},因为它不知道std::function类型列表。

我看到您已经在Ts...列表中说明了 first 类型,但这还不够,因为Ts...列表是一个可变的列表,因此编译器不会这样做。不知道Ts...列表中只有一种类型。

此问题的一个简单解决方案是将函数作为简单的Ts...类型推导。

否则,如果您用两个模板类型列表编写了F,则可以将callFunction()传递给函数

std::function

但我认为这不是令人满意的解决方案。