c ++ 11:如何编写一个包装器函数来创建`std :: function`对象 [英] c++11: How to write a wrapper function to make `std::function` objects
问题描述
我想写一个封装 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;
}
问题是你的代码不能正确处理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;
}
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屋!