2012-02-11 68 views
17

函数名为test以std :: function <>作为其参数。C++ 11 variadic std ::函数参数

template<typename R, typename ...A> 
void test(std::function<R(A...)> f) 
{ 
    // ... 
} 

但是,如果我做到以下几点:

void foo(int n) { /* ... */ } 

// ... 

test(foo); 

编译器(GCC 4.6.1)说no matching function for call to test(void (&)(int))

要使最后一行test(foo)编译并正常工作,我该如何修改test()函数?在test()函数中,我需要f,其类型为std :: function <>。

我的意思是说,是否有任何模板技巧让编译器确定函数的签名(例如foo),并自动将其转换为std::function<void(int)>

编辑

我想使这个工作lambda表达式(两人均表示,无国籍)为好。

回答

11

它看起来像你想使用重载

template<typename R, typename ...A> 
void test(R f(A...)) 
{ 
    test(std::function<R(A...)>(f)); 
} 

这种简单的实现,如果不是全部,你会尝试通过功能将接受最。异域功能将被拒绝(如void(int...))。更多的工作会给你更通用的。

+0

什么lambda表达式(两人均表示,无国籍)? – 2012-02-11 17:25:27

+4

@丹尼尔,你运气不好。或者让'test'接受任何东西('T')。无论如何,std :: function'不会完全拒绝不兼容的函数对象,所以限制函数模板参数类型的目标似乎对我来说并不是太有用。 – 2012-02-11 17:27:46

+1

我用'(T)'试过了,但是,怎么能'std :: function '?看来我可以使用'std :: result_of <>'来获得'R',但是'A ...'? – 2012-02-11 17:33:22

4

除非您处于'二元定界'(例如动态库,'不透明'API),否则通常不建议接受std::function,因为您刚刚目睹它们会对超载造成严重破坏。当函数实际上按值取std::function时,构造对象以避免重载问题(如果函数完全被重载)通常是调用者的负担。

由于您已经编写了模板,因此您可能并未使用std::function(作为参数类型)来获得类型删除的好处。如果你想要做的是检查任意仿函数,那么你需要一些特征。例如。 Boost.FunctionTypes具有诸如result_typeparameter_types的特征。一个最小的,功能例如:

#include <functional> 

#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 
#include <boost/function_types/function_type.hpp> 

template<typename Functor> 
void test(Functor functor) // accept arbitrary functor! 
{ 
    namespace ft = boost::function_types; 

    typedef typename ft::result_type<Functor>::type result_type; 
    typedef ft::parameter_types<Functor> parameter_types; 
    typedef typename boost::mpl::push_front< 
     parameter_types 
     , result_type 
    >::type sequence_type; 
    // sequence_type is now a Boost.MPL sequence in the style of 
    // mpl::vector<int, double, long> if the signature of the 
    // analyzed functor were int(double, long) 

    // We now build a function type out of the MPL sequence 
    typedef typename ft::function_type<sequence_type>::type function_type; 

    std::function<function_type> function = std::move(functor); 
} 

最后一点,我不建议反思函子(即督促其结果类型和参数类型)中作为根本就不多态仿函数工作的一般情况。考虑几个重载operator():那么没有'规范'的结果类型或参数类型。使用C++ 11时,最好'热切地'接受任何类型的函数,或者根据需要使用像SFINAE或static_assert这样的技术来限制它们,并且稍后(当参数可用时)使用std::result_of检查结果类型一组给定的参数。需要限制前方的情况是当目标是将仿函数存储到例如一个std::function<Sig>的容器。

为了体会上一段我的意思,它足以用多态函子来测试上面的代码片段。

5

std::function实现可调用接口,即它看起来像一个功能,但是,这并不意味着你应该需要调用的对象是std::function秒。

template< typename F > // accept any type 
void test(F const &f) { 
    typedef std::result_of< F(args) >::type R; // inspect with traits queries 
} 

鸭子打字是模板元编程中的最佳策略。当接受一个模板参数时,不具体,只需让客户端实现接口。

如果你真的需要例如std::function重新定向的变量或一些疯狂的那样,你知道的输入是原始函数指针,可以分解原始函数指针类型,它reconsitute成std::function

template< typename R, typename ... A > 
void test(R (*f)(A ...)) { 
    std::function< R(A ...) > internal(f); 
} 

,因为现在已经被封装在函数中,用户可以不通过std::function。您可以将现有的代码保留为另一个重载并委托给它,但要小心保持接口简单。

至于有状态lambda,我不知道如何处理这种情况。它们不会分解为函数指针,并且据我所知,参数类型不能被查询或推断。这个信息对于实例化std::function是必要的,好或坏。

+1

我认为正确的术语是动态绑定 - 使用术语'duck typing'已被弃用 – serup 2017-01-26 11:34:07

+0

@serup Google =>“后期绑定或动态绑定是一种计算机编程机制,或者用参数调用的函数在运行时按名称查找。“不适用于模板。 – Potatoswatter 2017-01-26 11:52:17

+0

那么为什么要使用术语鸭打字? – serup 2017-01-26 12:04:56

2

这是一个旧的,我似乎无法找到很多关于同一主题,所以我想我会继续并在一个说明。

编译于GCC 4.8.2,以下工作:

template<typename R, typename... A> 
R test(const std::function<R(A...)>& func) 
{ 
    // ... 
} 

但是,你不能仅仅通过传递你的指针,lambda表达式等。但是,下面的两个例子都工作,把它它:

test(std::function<void(int, float, std::string)>(
     [](int i, float f, std::string s) 
     { 
      std::cout << i << " " << f << " " << s << std::endl; 
     })); 

另外:

void test2(int i, float f, std::string s) 
{ 
    std::cout << i << " " << f << " " << s << std::endl; 
} 

// In a function somewhere: 
test(std::function<void(int, float, std::string)>(&test2)); 

的这些缺点应该站出来很明显:哟你必须为它们显式声明std :: function,这可能看起来有点难看。这就是说,虽然我把它与一个元组一起抛出,它被扩展为调用传入函数,并且它可以工作,只需要多一点的明确说明你在做什么调用测试函数。

示例代码,包括元组的事情,如果你想用它玩:http://ideone.com/33mqZA

+1

为了踢球,我想出了一个使用index_sequence(在C++ 14中添加,但易于在C++ 11中实现)的方法和一个function_traits风格的结构来提供一些东西这将采取任何lambda,仿函数或函数,并使用它。 [http://ideone.com/LNpj74](http://ideone.com/LNpj74)显示了一个工作的例子。但是,请注意,对于使用2+运算符()重载的函子,它需要额外的接口来指定要使用的类型。 – user3694249 2014-06-09 02:21:00

+0

正如Potatoswatter在上面提到的那样,当模板可用时,使用'std :: function'是一堆动态分配开销和样板文件没有任何好处。更好的办法是制作一个可以接受lambda的通用函数模板,而不是将'std :: function'全部拉入。 – 2016-08-27 16:36:33