下面的最小代码给出了编译错误:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
{
}
int main(int argc, char *argv[])
{
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
return 0;
}
错误是:
foo.cc: In function ‘int main(int, char**)’:
foo.cc:13:69: error: no matching function for call to ‘print_action(std::function<std::basic_string<char>*(unsigned char, std::basic_string<char>)>)’
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
^
foo.cc:13:69: note: candidate is:
foo.cc:7:6: note: template<class ActionType, class ... Cols> void print_action(std::function<ActionType*(Cols ..., ActionType)>)
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
^
foo.cc:7:6: note: template argument deduction/substitution failed:
foo.cc:13:69: note: mismatched types ‘std::basic_string<char>’ and ‘unsigned char’
print_action<string, uint8_t>(function<string*(uint8_t, string)>());
^
foo.cc:13:69: note: ‘std::function<std::basic_string<char>*(unsigned char, std::basic_string<char>)>’ is not derived from ‘std::function<std::basic_string<char>*(Cols ..., std::basic_string<char>)>’
我还尝试通过下面的代码将输入参数更改为简单的指针函数:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
void print_action(ActionType*(*action_factory)(Cols..., ActionType))
{
}
string* foo_factory(uint8_t cols, string act)
{
}
int main(int argc, char *argv[])
{
print_action<string, uint8_t>(foo_factory);
return 0;
}
它给了我同样的错误。经过一些工作我最后的猜测是它是g ++的一个bug,因为如果我将variadic模板参数更改为一个简单的参数,就不会发生错误。 我是对的还是我错过了一些c ++的语法?
我使用g ++ - 4.8.4和c ++ 11标志(我使用clang-3.4和g ++ - 4.9.2检查它。)
修改
如果我将代码更改为:
#include <iostream>
#include <functional>
using namespace std;
template<typename ActionType, typename... Cols>
struct Foo
{
Foo()
{}
void print_action(function<ActionType*(Cols..., ActionType)> action_factory)
{
}
};
int main(int argc, char *argv[])
{
Foo<string, uint8_t> f;
f.print_action(function<string*(uint8_t, string)>());
return 0;
}
我没有错误。我不明白这种行为,因为在这两种情况下我都明确定义了模板参数,我没想到任何推论,但似乎编译器在它是模板函数时做了一些推论,但当它是模板的成员函数时却没有类。
答案 0 :(得分:3)
问题是你有(Cols..., ActionType)
。有人可能认为编译器应该注意到Cols...
应该是结尾之前的所有参数,只要结尾与ActionType
相同,但这不是语言的工作方式。
一个简单的解决方案就是推导出整个参数列表。如果您碰巧以类型不支持的方式使用最终参数,则无论如何编译都将失败,您始终可以添加static_assert
以确保最终参数与{{1}相同如果你真的想要。
ActionType
答案 1 :(得分:2)
通常会编写可变参数模板
template<typename First, typename... Rest> class test;
编译器匹配第一个参数,并将其余参数(空或更多)留给可变参数部分。逆转时的行为与预期不符。可变参数模板参数是 greedy ,因为所有参数都被它吃掉,没有任何参数被写到最后。
当订单颠倒时,您的示例代码编译正常:
template<typename ActionType, typename... Cols>
void print_action(function<ActionType*(ActionType, Cols...)>) {
}
int main()
{
print_action(function<string*(string, uint8_t)>());
}
参数类型推导和实例化之间存在差异。引自C++ Templates: The Complete Guide:
按具体类型替换模板参数的过程称为实例化。它会生成一个模板实例。
当实例化一个函数模板时,我们得到一个函数;类/结构相同。
似乎编译器在它是模板函数时会做一些推论,但在它是模板类的成员函数时却不会。
函数调用没有类型推导或实例化。它不是一个功能模板,而只是一个功能。该调用只是另一个普通的函数调用。
但是,struct
实际上是struct
模板,并且在创建对象时,会从模板中创建struct
。对于此struct
模板实例化
template<typename ActionType, typename... Cols>
struct Foo;
订单正确(可变参数是最后一个),因此它可以工作。