使用std :: function参数调用函数时编译错误

时间:2016-01-07 12:20:54

标签: c++11 g++

下面的最小代码给出了编译错误:

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

我没有错误。我不明白这种行为,因为在这两种情况下我都明确定义了模板参数,我没想到任何推论,但似乎编译器在它是模板函数时做了一些推论,但当它是模板的成员函数时却没有类。

2 个答案:

答案 0 :(得分:3)

问题是你有(Cols..., ActionType)。有人可能认为编译器应该注意到Cols...应该是结尾之前的所有参数,只要结尾与ActionType相同,但这不是语言的工作方式。

一个简单的解决方案就是推导出整个参数列表。如果您碰巧以类型不支持的方式使用最终参数,则无论如何编译都将失败,您始终可以添加static_assert以确保最终参数与{{1}相同如果你真的想要。

ActionType

Live Demo

答案 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)>());
}

Live example

参数类型推导和实例化之间存在差异。引自C++ Templates: The Complete Guide

  

按具体类型替换模板参数的过程称为实例化。它会生成一个模板实例。

当实例化一个函数模板时,我们得到一个函数;类/结构相同。

  

似乎编译器在它是模板函数时会做一些推论,但在它是模板类的成员函数时却不会。

函数调用没有类型推导或实例化。它不是一个功能模板,而只是一个功能。该调用只是另一个普通的函数调用。

但是,struct实际上是struct模板,并且在创建对象时,会从模板中创建struct。对于此struct模板实例化

template<typename ActionType, typename... Cols>
struct Foo;

订单正确(可变参数是最后一个),因此它可以工作。