C++ 创建泛型 std::bind 的 std::packaged_task
C++ create an std::packaged_task of a generic std::bind
我只是尝试为给定的 std::bind
创建一个 std::packaged_task
。
#include <functional>
#include <future>
class A
{
public:
template<class T>
void execute(T func)
{
std::packaged_task<T> task(func);
}
};
int main()
{
auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);
}
main.cpp: In instantiation of 'void A::execute(T) [with T =
std::_Bind(int)>]': main.cpp:20:20: required
from here main.cpp:10:36: error:
'std::packaged_task(int)> > task' has
incomplete type
std::packaged_task task(func);
我正在使用 G++ 5.2.0 和 C++14。
有人有想法吗?
谢谢,
从 c++11 开始,无法将函数参数声明为 auto(我相信它是 c++17 的一部分)。不幸的是,我没有支持该功能的编译器,我不太确定它的语义,但解决方法是使用模板:
#include <future>
#include <functional>
template<class T>
void execute(std::function<T> func) // is a std::bind
{
std::packaged_task<T> task(func);
//...
}
你可以这样使用:
std::function<int()> func = std::bind([](int x) { return x*x; }, 5);
execute(func);
出现此错误的原因是 std::packaged_task
仅专用于 ReturnType(Args...)
形式的模板参数。因此,如果您传递不同的东西,例如从 std::bind
返回的 std::_Binder
或 std::function
,它会显示为未定义。
编辑
关于您关于推断 std::function 类型的评论:
您可以使用 lambda 代替 std::bind()
。
如果您有一个 class A
函数 doSomething(int x)
就像您评论中的那样:
class A
{
public:
int doSomething(int x)
{
return x *x;
}
};
我们需要将执行函数更改为以下内容(参见 Yakk 的回答):
template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
using zF = std::decay_t<F>&&;
using R = std::result_of_t< zF(Args...) >;
std::packaged_task<R()> task(
std::bind([func = std::forward<F>(func)](auto&&...args)mutable->R{
return std::move(func)(decltype(args)(args)...);
}, std::forward<Args>(args)...)
);
}
现在你可以像这样使用它了:
A a;
auto f = [](A& inst, int x) { return inst.doSomething(x); };
execute(f, a, 5);
// or
auto g = [&]() { return a.doSomething(5); };
execute(g);
注意
注意 a
的生命周期,因为 packaged_task 可能 运行 异步。
这是一个简单的解决方案:
template<class...Args, class F>
void execute(F&& func)
{
using R = std::result_of_t< std::decay_t<F>(Args...) >;
std::packaged_task<R(Args...)> task(std::forward<F>(func));
}
我添加了完美转发,以及传入参数的选项。如果您不传入参数,它会假定传入的可调用函数采用 0 个参数。
使用示例:
auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);
如果 func
需要参数,您必须显式传递它们。这是公平的,因为很难在不知道它期望的参数的情况下使用 std::packaged_task
。 ;)
这也编译:
auto func = [](int x) { return x*x; };
A name;
name.execute<int>(func);
但是您将如何使用该打包任务很棘手。
如果你想模仿std::async
之类的界面,我们可以这样做:
template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
using zF = std::decay_t<F>&&;
using R = std::result_of_t< zF(Args...) >;
std::packaged_task<R()> task(
std::bind( [func=std::forward<F>(func)](auto&&...args)mutable->R{
return std::move(func)(decltype(args)(args)...);
}, std::forward<Args>(args)... )
);
}
将传入的 func
小心地包装在 lambda 中,以避免在 bind
上调用 bind
,然后绑定传入的参数。
现在您可以:
name.execute([](int x){ return x*x; }, 5);
根本不使用 bind
。
上面的代码没有经过编译,可能包含tpyos。
我只是尝试为给定的 std::bind
创建一个 std::packaged_task
。
#include <functional>
#include <future>
class A
{
public:
template<class T>
void execute(T func)
{
std::packaged_task<T> task(func);
}
};
int main()
{
auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);
}
main.cpp: In instantiation of 'void A::execute(T) [with T = std::_Bind(int)>]': main.cpp:20:20: required from here main.cpp:10:36: error: 'std::packaged_task(int)> > task' has incomplete type std::packaged_task task(func);
我正在使用 G++ 5.2.0 和 C++14。
有人有想法吗?
谢谢,
从 c++11 开始,无法将函数参数声明为 auto(我相信它是 c++17 的一部分)。不幸的是,我没有支持该功能的编译器,我不太确定它的语义,但解决方法是使用模板:
#include <future>
#include <functional>
template<class T>
void execute(std::function<T> func) // is a std::bind
{
std::packaged_task<T> task(func);
//...
}
你可以这样使用:
std::function<int()> func = std::bind([](int x) { return x*x; }, 5);
execute(func);
出现此错误的原因是 std::packaged_task
仅专用于 ReturnType(Args...)
形式的模板参数。因此,如果您传递不同的东西,例如从 std::bind
返回的 std::_Binder
或 std::function
,它会显示为未定义。
编辑
关于您关于推断 std::function 类型的评论:
您可以使用 lambda 代替 std::bind()
。
如果您有一个 class A
函数 doSomething(int x)
就像您评论中的那样:
class A
{
public:
int doSomething(int x)
{
return x *x;
}
};
我们需要将执行函数更改为以下内容(参见 Yakk 的回答):
template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
using zF = std::decay_t<F>&&;
using R = std::result_of_t< zF(Args...) >;
std::packaged_task<R()> task(
std::bind([func = std::forward<F>(func)](auto&&...args)mutable->R{
return std::move(func)(decltype(args)(args)...);
}, std::forward<Args>(args)...)
);
}
现在你可以像这样使用它了:
A a;
auto f = [](A& inst, int x) { return inst.doSomething(x); };
execute(f, a, 5);
// or
auto g = [&]() { return a.doSomething(5); };
execute(g);
注意
注意 a
的生命周期,因为 packaged_task 可能 运行 异步。
这是一个简单的解决方案:
template<class...Args, class F>
void execute(F&& func)
{
using R = std::result_of_t< std::decay_t<F>(Args...) >;
std::packaged_task<R(Args...)> task(std::forward<F>(func));
}
我添加了完美转发,以及传入参数的选项。如果您不传入参数,它会假定传入的可调用函数采用 0 个参数。
使用示例:
auto func = std::bind([](int x) { return x*x; }, 5);
A name;
name.execute(func);
如果 func
需要参数,您必须显式传递它们。这是公平的,因为很难在不知道它期望的参数的情况下使用 std::packaged_task
。 ;)
这也编译:
auto func = [](int x) { return x*x; };
A name;
name.execute<int>(func);
但是您将如何使用该打包任务很棘手。
如果你想模仿std::async
之类的界面,我们可以这样做:
template<class F, class...Args>
void execute(F&& func, Args&&...args)
{
using zF = std::decay_t<F>&&;
using R = std::result_of_t< zF(Args...) >;
std::packaged_task<R()> task(
std::bind( [func=std::forward<F>(func)](auto&&...args)mutable->R{
return std::move(func)(decltype(args)(args)...);
}, std::forward<Args>(args)... )
);
}
将传入的 func
小心地包装在 lambda 中,以避免在 bind
上调用 bind
,然后绑定传入的参数。
现在您可以:
name.execute([](int x){ return x*x; }, 5);
根本不使用 bind
。
上面的代码没有经过编译,可能包含tpyos。