c ++ 11:如何编写一个包装器函数来创建`std :: function`对象 [英] c++11: How to write a wrapper function to make `std::function` objects

查看:127
本文介绍了c ++ 11:如何编写一个包装器函数来创建`std :: function`对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写一个封装 make_function ,它像 std :: make_pair 可以创建一个 std :: function 对象。



就像 make_pair ,对于函数指针 foo auto f0 = make_function(foo); 创建 std :: function 函数对象 f0 的正确类型签名。
只是为了澄清,我不介意偶尔给类型参数 make_function ,如果很难(或不可能)从参数完全推导出类型。



到目前为止,我想到的代码(下面的代码)适用于lambdas,一些函数指针和函数(我没有考虑挥发物)。但我不能得到它的工作 std :: bind std :: bind< R> 在下面的代码中

  auto f2 = make_function(std :: bind(foo,_1,_2,_3)); // not OK 

无法编译/工作,使用gcc 4.8.1。我猜我没有捕获操作符() bind 结果正确,但我不确定如何解决。



任何帮助如何解决这种情况或改善在其他角落的情况下。



我的问题是,当然,如何解决在下面的例子中的错误。



对于背景,我使用这个包装器的一个案例可以在这个问题:如何使C ++ 11功能接收功能<参数自动接受lambdas 。如果您不允许使用 std :: function 或我的具体使用方式,请留下您的意见,并在这里讨论技术问题。 >

--- EDIT ---



根据一些意见,这是因为歧义问题( std :: bind 结果的函数调用operator()的模糊性)。正如@Mooing Duck的答案所指出的,解决方案是明确给出参数类型。我已经更新了代码以组合在@Mooing Duck的答案(稍微改变类型参数)的三个函数,使得 make_function 包装器现在可以处理/类型推导明确



(对于明确的情况,我的原始代码是:http://stackoverflow.com/a/21665705/683218 ,并可在以下位置进行测试: https://ideone.com/UhAk91 ):

  #include< functional> 
#include< utility>
#include< iostream>
#include< functional>
using namespace std;

//对于作为函子的通用类型,委托给它的'operator()'
template< typename T>
struct function_traits
:public function_traits< decltype(& T :: operator())>
{};

//指向成员函数的指针
template< typename ClassType,typename ReturnType,typename ... Args>
struct function_traits< ReturnType(ClassType :: *)(Args ...)const> {
enum {arity = sizeof ...(Args)};
typedef function< ReturnType(Args ...)> f_type;
};

//指向成员函数的指针
template< typename ClassType,typename ReturnType,typename ... Args>
struct function_traits< ReturnType(ClassType :: *)(Args ...)> {
enum {arity = sizeof ...(Args)};
typedef function< ReturnType(Args ...)> f_type;
};

//用于函数指针
template< typename ReturnType,typename ... Args>
struct function_traits< ReturnType(*)(Args ...)> {
enum {arity = sizeof ...(Args)};
typedef function< ReturnType(Args ...)> f_type;
};

template< typename L>
static typename function_traits< L> :: f_type make_function(L l){
return(typename function_traits L :: fl_type)(1)
}

//处理bind&多个函数调用operator()的
模板< typename ReturnType,typename ... Args,class T>
auto make_function(T&& t)
- > std :: function< decltype(ReturnType(t(std :: declval< Args>()...)))(Args ...)
{return {std :: forward< T>(t)};}

//处理显式重载
template< typename ReturnType,typename ... Args>
auto make_function(ReturnType(* p)(Args ...))
- > std :: function< ReturnType(Args ...)> {
return {p};
}

//处理显式重载
template< typename ReturnType,typename ... Args,typename ClassType>
auto make_function(ReturnType(ClassType :: * p)(Args ...))
- > std :: function< ReturnType(Args ...)> {
return {p};
}

//测试
使用命名空间std :: placeholder;

int foo(int x,int y,int z){return x + y + z;}
int foo1(int x,int y,int z){return x + y + z;}
float foo1(int x,int y,float z){return x + y + z;}

int main(){
// unambuiguous
auto f0 = make_function(foo);
auto f1 = make_function([](int x,int y,int z){return x + y + z;});
cout<< make_function([](int x,int y,int z){return x + y + z;})(1,2,3)< endl;

int first = 4;
auto lambda_state = [=](int y,int z){return first + y + z;}; // lambda with states
cout<< make_function(lambda_state)(1,2)< endl;

//模糊的情况
auto f2 = make_function< int,int,int,int>(std :: bind(foo,_1,_2,_3) //绑定结果有多个operator()重载
cout<< f2(1,2,3) endl;
auto f3 = make_function< int,int,int,int>(foo1); // overload1
auto f4 = make_function< float,int,int,float>(foo1); // overload2

return 0;
}

Ideone

解决方案

问题是你的代码不能正确处理lambdas,bind或functionoid,所有这些都没有参数。要处理这些,你必须指定参数类型:

  //纯函数指针
template< ... Args,typename ReturnType>
auto make_function(ReturnType(* p)(Args ...))
- > std :: function< ReturnType(Args ...)>
{return {p};}

// nonconst成员函数指针
template< typename ... Args,typename ReturnType,typename ClassType>
auto make_function(ReturnType(ClassType :: * p)(Args ...))
- > std :: function< ReturnType(Args ...)>
{return {p};}

// const成员函数指针
template< typename ... Args,typename ReturnType,typename ClassType>
auto make_function(ReturnType(ClassType :: * p)(Args ...)const)
- > std :: function< ReturnType(Args ...)>
{return {p};}

//合格的functionoids
模板< typename FirstArg,typename ... Args,class T&
auto make_function(T&& t)
- > std :: function< decltype(t(std :: declval< FirstArg>(),std :: declval< Args>()...))(FirstArg,Args ...)
{return {std :: forward< T>(t)};}

//不合格的functionoids试图推导出`T :: operator()`的签名并使用它。
template< class T>
auto make_function(T&& t)
- > decltype(make_function(& std :: remove_reference< T> :: type :: operator()))
{return {std :: forward< T>(t)};}

变量:

  int func (int x,int y,int z){return x + y + z;} 
int重载(char x,int y,int z){return x + y + z;}
int重载(int x,int y,int z){return x + y + z;}
struct functionoid {
int operator() z;}
};
struct functionoid_overload {
int operator()(int x,int y,int z){return x + y + z;}
int operator z){return x + y + z;}
};
int first = 0;
auto lambda = [](int x,int y,int z){return x + y + z;};
auto lambda_state = [=](int x,int y,int z){return x + y + z + first;};
auto bound = std :: bind(func,_1,_2,_3);

测试:

 code> std :: function< int(int,int,int)> f0 = make_function(func); assert(f0(1,2,3)== 6); 
std :: function< int(char,int,int)> f1 = make_function< char,int,int>(overloaded);断言(f1(1,2,3)== 6);
std :: function< int(int,int,int)> f2 = make_function< int,int,int>(overloaded); assert(f2(1,2,3)== 6);
std :: function< int(int,int,int)> f3 = make_function(lambda);断言(f3(1,2,3)== 6);
std :: function< int(int,int,int)> f4 = make_function(lambda_state); assert(f4(1,2,3)== 6);
std :: function< int(int,int,int)> f5 = make_function< int,int,int>(bound); assert(f5(1,2,3)== 6);
std :: function< int(int,int,int)> f6 = make_function(functionoid {}); assert(f6(1,2,3)== 6);
std :: function< int(int,int,int)> f7 = make_function< int,int,int>(functionoid_overload {}); assert(f7(1,2,3)== 6);
std :: function< int(char,int,int)> f8 = make_function< char,int,int>(functionoid_overload {}); assert(f8(1,2,3)== 6);

http://coliru.stacked-crooked.com/a/a9e0ad2a2da0bf1f 你的lambda成功的唯一原因是因为它是隐式转换为一个函数指针,因为你的例子不捕获任何州。注意,我的代码需要重载函数的参数类型,重载operator()的函数类型(包括绑定),但现在可以推导出所有非重载的函数。



decltype 行很复杂,但它们用于推导返回类型。注意,在我的测试的NONE中,我需要指定返回类型。让我们将 make_function< short,int,int> 分解成 T *)(short,int,int):

   - > decltype(t(std :: declval< FirstArg>(),std :: declval< Args>()...))(FirstArg,Args ...)
`std :: declval< FirstArg> `是`short {}`(大致)
- > decltype(t(short {},std :: declval< Args>()...))(FirstArg,Args ...)
`std :: declval& int {},int {}`(rough)
- > decltype(t(short {},int {},int {})(FirstArg,Args ...)
`t(short {},int {},int {})`` `(大致)
- > decltype(short {})(FirstArg,Args ...)
`decltype(int {})`是`int`
- > int FirstArg,Args ...)
`FirstArg`仍然是`short`
- > int(short,Args ...)
`Args ...` `
- > int(short,int,int)
所以这个复杂的表达式只是简单的计算出函数的签名
,看起来很熟悉...


I am trying to write a wrapper make_function, which like std::make_pair can create a std::function object out of suitable callable objects.

Just like make_pair, for a function pointer foo, auto f0 = make_function(foo); creates a std::function function object f0 of the right type signature. Just to clarify, I don't mind occasionally giving type parameters to make_function in case it is difficult (or impossible) to deduce the type entirely from the parameters.

What I came up with so far (code below) works fine for lambdas, some function pointers, and functors (I didn't consider volatiles). But I couldn't get it work for std::bind or std::bind<R> results. In the code below

auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK

wouldn't compile/work, with gcc 4.8.1. I am guessing that I didn't capture the operator() for the bind result correctly, but I am not sure how to fix it.

Any help on how to fix this case or improvement in other corner cases is appreciated.

My question is, of course, how to fix the error in the example below.

For background, one of the cases I use this wrapper can be found at this question: How to make C++11 functions taking function<> parameters accept lambdas automatically. If you do not approve the use of std::function or my specific way of using it, please leave your comments in that post, and discuss technical issues here.

--- EDIT ---

From some of the comments, I learned that it's because of the ambiguity issue (ambiguity of the function call operator() of std::bind results). As pointed out by @Mooing Duck's answer, the solution is to give the parameter types explicitly. I have updated the code to combine the three functions in @Mooing Duck's answer (with slight change of type parameters), so that the make_function wrapper can now handle/type-deduce unambiguous cases as before, and allow specification of complete type signature when there is ambiguity.

(My original code for the unambiguous cases is at: http://stackoverflow.com/a/21665705/683218 and can be tested at: https://ideone.com/UhAk91):

#include <functional>
#include <utility>
#include <iostream>
#include <functional>
using namespace std;

// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
  : public function_traits<decltype(&T::operator())>
{};

// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
  enum { arity = sizeof...(Args) };
  typedef function<ReturnType (Args...)> f_type;
};

// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
  enum { arity = sizeof...(Args) };
  typedef function<ReturnType (Args...)> f_type;
};

// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)>  {
  enum { arity = sizeof...(Args) };
  typedef function<ReturnType (Args...)> f_type;
};

template <typename L> 
static typename function_traits<L>::f_type make_function(L l){
  return (typename function_traits<L>::f_type)(l);
}

//handles bind & multiple function call operator()'s
template<typename ReturnType, typename... Args, class T>
auto make_function(T&& t) 
  -> std::function<decltype(ReturnType(t(std::declval<Args>()...)))(Args...)> 
{return {std::forward<T>(t)};}

//handles explicit overloads
template<typename ReturnType, typename... Args>
auto make_function(ReturnType(*p)(Args...))
    -> std::function<ReturnType(Args...)> {
  return {p};
}

//handles explicit overloads
template<typename ReturnType, typename... Args, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...)) 
    -> std::function<ReturnType(Args...)> { 
  return {p};
}

// testing
using namespace std::placeholders;

int foo(int x, int y, int z) { return x + y + z;}
int foo1(int x, int y, int z) { return x + y + z;}
float foo1(int x, int y, float z) { return x + y + z;}

int main () {
  //unambuiguous
  auto f0 = make_function(foo);
  auto f1 = make_function([](int x, int y, int z) { return x + y + z;});
  cout << make_function([](int x, int y, int z) { return x + y + z;})(1,2,3) << endl;

  int first = 4;
  auto lambda_state = [=](int y, int z) { return first + y + z;}; //lambda with states
  cout << make_function(lambda_state)(1,2) << endl;

  //ambuiguous cases
  auto f2 = make_function<int,int,int,int>(std::bind(foo,_1,_2,_3)); //bind results has multiple operator() overloads
  cout << f2(1,2,3) << endl;
  auto f3 = make_function<int,int,int,int>(foo1);     //overload1
  auto f4 = make_function<float,int,int,float>(foo1); //overload2

  return 0;
}

Ideone

解决方案

The problem is your code doesn't handle lambdas, bind, or functionoids properly, your code assumes that all of these take no parameters. To handle these, you'll have to specify the parameter types:

//plain function pointers
template<typename... Args, typename ReturnType>
auto make_function(ReturnType(*p)(Args...))
    -> std::function<ReturnType(Args...)> 
{return {p};}

//nonconst member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...)) 
    -> std::function<ReturnType(Args...)>
{return {p};}

//const member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...) const) 
    -> std::function<ReturnType(Args...)>
{return {p};}

//qualified functionoids
template<typename FirstArg, typename... Args, class T>
auto make_function(T&& t) 
    -> std::function<decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)> 
{return {std::forward<T>(t)};}

//unqualified functionoids try to deduce the signature of `T::operator()` and use that.
template<class T>
auto make_function(T&& t) 
    -> decltype(make_function(&std::remove_reference<T>::type::operator())) 
{return {std::forward<T>(t)};}

Variables:

int func(int x, int y, int z) { return x + y + z;}
int overloaded(char x, int y, int z) { return x + y + z;}
int overloaded(int x, int y, int z) { return x + y + z;}
struct functionoid {
    int operator()(int x, int y, int z) { return x + y + z;}
};
struct functionoid_overload {
    int operator()(int x, int y, int z) { return x + y + z;}
    int operator()(char x, int y, int z) { return x + y + z;}
};
int first = 0;
auto lambda = [](int x, int y, int z) { return x + y + z;};
auto lambda_state = [=](int x, int y, int z) { return x + y + z + first;};
auto bound = std::bind(func,_1,_2,_3);

Tests:

std::function<int(int,int,int)> f0 = make_function(func); assert(f0(1,2,3)==6);
std::function<int(char,int,int)> f1 = make_function<char,int,int>(overloaded); assert(f1(1,2,3)==6);
std::function<int(int,int,int)> f2 = make_function<int,int,int>(overloaded); assert(f2(1,2,3)==6);
std::function<int(int,int,int)> f3 = make_function(lambda); assert(f3(1,2,3)==6);
std::function<int(int,int,int)> f4 = make_function(lambda_state); assert(f4(1,2,3)==6);
std::function<int(int,int,int)> f5 = make_function<int,int,int>(bound); assert(f5(1,2,3)==6);
std::function<int(int,int,int)> f6 = make_function(functionoid{}); assert(f6(1,2,3)==6);
std::function<int(int,int,int)> f7 = make_function<int,int,int>(functionoid_overload{}); assert(f7(1,2,3)==6);
std::function<int(char,int,int)> f8 = make_function<char,int,int>(functionoid_overload{}); assert(f8(1,2,3)==6);

http://coliru.stacked-crooked.com/a/a9e0ad2a2da0bf1f The only reason your lambda was succeeding is because it was implicitly convertible to a function pointer because your example doesn't capture any state. Note that my code requires the parameter types for overloaded functions, functionoids with overloaded operator() (including bind), but is now able to deduce all non-overloaded functionoids.

The decltype lines are complicated but they're used to deduce the return types. Notice that in NONE of my tests do I need to specify the return type. Let's break down make_function<short,int,int> down as if T is char(*)(short, int, int):

-> decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)
`std::declval<FirstArg>()` is `short{}` (roughly)
-> decltype(t(short{}, std::declval<Args>()...))(FirstArg, Args...)
`std::declval<Args>()...` are `int{}, int{}` (roughly)
-> decltype(t(short{}, int{}, int{})(FirstArg, Args...)
`t(short{}, int{}, int{})` is an `int{}` (roughly)
-> decltype(short{})(FirstArg, Args...)
`decltype(int{})` is `int`
-> int(FirstArg, Args...)
`FirstArg` is still `short`
-> int(short, Args...)
`Args...` are `int, int`
-> int(short, int, int)
So this complex expression merely figures out the function's signature
well, that should look familiar...

这篇关于c ++ 11:如何编写一个包装器函数来创建`std :: function`对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆