c ++ 11:如何编写包装函数来生成`std :: function`对象

tin*_*lyx 9 c++ c++11 std-function c++14

我正在尝试编写一个包装器make_function,它std::make_pair可以std::function用合适的可调用对象创建一个对象.

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

到目前为止我提出的(下面的代码)适用于lambdas,一些函数指针和函子(我没有考虑挥发性).但我无法让它起作用std::bindstd::bind<R>结果.在下面的代码中

auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK
Run Code Online (Sandbox Code Playgroud)

不会编译/工作,使用gcc 4.8.1.我猜测,我没有捕捉到operator()bind正确的结果,但我不知道如何解决它.

任何有关如何解决此案例或改善其他角落案件的帮助表示赞赏.

当然,我的问题是如何修复下面示例中的错误.

对于后台,我可以在这个问题中找到我使用此包装器的一种情况:如何使C++ 11函数采用函数<>参数自动接受lambdas.如果您不批准使用std::function或以我的具体使用方式,请在该帖子中留下您的意见,并在此处讨论技术问题.

---编辑---

从一些评论中,我了解到这是因为模糊性问题(std::bind结果函数调用operator()的模糊性).正如@Mooing Duck的回答所指出的,解决方案是明确地给出参数类型.我已经更新了代码以结合@Mooing Duck的答案中的三个函数(稍微更改了类型参数),这样make_function包装器现在可以像以前一样处理/类型推导明确的情况,并允许指定完整类型签名歧义.

(我的明确案例的原始代码位于:https://stackoverflow.com/a/21665705/683218,可在以下网址进行测试: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;
}
Run Code Online (Sandbox Code Playgroud)

Ideone

Moo*_*uck 7

问题是你的代码没有正确处理lambdas,bind或functionoids,你的代码假设所有这些代码都没有参数.要处理这些,您必须指定参数类型:

//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)};}
Run Code Online (Sandbox Code Playgroud)

变量:

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);
Run Code Online (Sandbox Code Playgroud)

测试:

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);
Run Code Online (Sandbox Code Playgroud)

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

这些decltype行很复杂,但它们用于推断返回类型.请注意,在我的测试中,我需要指定返回类型.让我们分解make_function<short,int,int>,好像Tchar(*)(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...
Run Code Online (Sandbox Code Playgroud)


Nev*_*vin 6

一般来说,如果没有严格的限制,你无法解决它,无论你传递给什么,make_function只能用一个签名来调用.

你打算做什么,比如:

struct Generic
{
    void operator()() { /* ... */ }
    void operator()() const { /* ... */ }

    template<typename T, typename... Ts>
    T operator()(T&& t, Ts&&...) { /* ... */ }

    template<typename T, typename... Ts>
    T operator()(T&& t, Ts&&...) const { /* ... */ }
};
Run Code Online (Sandbox Code Playgroud)

C++ 14通用lambdas会有同样的问题.

签名std::function取决于您打算如何调用它而不是您如何构造/分配它.

你无法解决它std::bind,因为它具有无限期:

void foo() { std::cout << "foo()" << std::endl; }
//...

auto f = std::bind(foo);
f();                 // writes "foo()"
f(1);                // writes "foo()"
f(1, 2, 3, 4, 5, 6); // writes "foo()"
Run Code Online (Sandbox Code Playgroud)