【发布时间】:2014-02-12 20:21:15
【问题描述】:
我正在尝试编写一个包装器make_function,它像std::make_pair 一样可以从合适的可调用对象中创建一个std::function 对象。
就像make_pair,对于函数指针foo,auto f0 = make_function(foo); 创建一个正确类型签名的std::function 函数对象f0。
澄清一下,我不介意偶尔给make_function 提供类型参数,以防很难(或不可能)从参数中完全推断出类型。
到目前为止,我想出的东西(下面的代码)适用于 lambdas、一些函数指针和仿函数(我没有考虑 volatiles)。但我无法让它适用于std::bind 或std::bind<R> 结果。在下面的代码中
auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK
使用 gcc 4.8.1 无法编译/工作。我猜我没有正确捕获operator() 的bind 结果,但我不确定如何修复它。
感谢任何有关如何解决此案例或改进其他极端案例的帮助。
我的问题当然是如何修复下面示例中的错误。
作为背景,我使用这个包装器的一个案例可以在这个问题上找到:How to make C++11 functions taking function<> parameters accept lambdas automatically。如果您不同意使用std::function 或我的具体使用方式,请将您的 cmets 留在该帖子中,并在此处讨论技术问题。
--- 编辑---
从一些 cmets 中,我了解到这是因为歧义问题(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;
}
【问题讨论】:
-
一开始为什么要这样做?
auto f = std::bind(foo, _1, _2);比同等的std::function好! -
原因是我需要为函数参数输入
f为std::function。并且std::function类型变量不自动接受bind结果或其他可调用对象。 -
“std::function 类型变量不接受绑定结果或其他可自动调用的变量”是什么意思:coliru.stacked-crooked.com/a/d66117115e71a7ee?
-
你一直在寻求一些既不可能很好解决的问题(黑客解决方案在 C++1y 中会变得越来越糟糕),而且对于我所拥有的几乎所有实际案例都有错误的解决方案检查它试图解决的地方。它不能解决 lambdas,因为
make_function([](auto x, auto y, auto z) { return x + y + z;})失败(欢迎使用 C++1y!) -
完成任务很不错,IMO
make_int和make_double怎么样?make_string?make_vector?问题仍然存在:为什么你想要一个推断std::function?除了弄清楚你是否能实现它之外,你还有其他需要吗?您真正要做的是应用类型擦除,并支付相关费用,没有特别的原因。除了脑力练习之外,您的主要目标是编写一个会降低应用程序性能的复杂机制。
标签: c++ c++11 c++14 std-function