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::bind或std::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)
问题是你的代码没有正确处理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>,好像T是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...
Run Code Online (Sandbox Code Playgroud)
一般来说,如果没有严格的限制,你无法解决它,无论你传递给什么,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)
| 归档时间: |
|
| 查看次数: |
3427 次 |
| 最近记录: |