C++ 如何确定捕获不可复制参数的lambda的类型?
给定下面的不可复制任务类和示例代码C++ 如何确定捕获不可复制参数的lambda的类型?,c++,lambda,move,noncopyable,C++,Lambda,Move,Noncopyable,给定下面的不可复制任务类和示例代码 #include <functional> #include <iostream> #include <string> class Task { public: Task() { } Task(const Task& other) = delete; Task& operator=(const Task& other) = delete; Task
#include <functional>
#include <iostream>
#include <string>
class Task
{
public:
Task()
{
}
Task(const Task& other) = delete;
Task& operator=(const Task& other) = delete;
Task(Task&& other) = default;
Task& operator=(Task&& other) = default;
void operator()() const
{
std::cout << "Task !" << std::endl;
}
};
int main()
{
auto task = Task();
auto lambda = [task = std::move(task)]
{
task();
};
std::function<void()> test = std::move(lambda);
test();
}
#包括
#包括
#包括
课堂任务
{
公众:
任务()
{
}
任务(常量任务和其他)=删除;
任务和运算符=(常量任务和其他)=删除;
任务(任务和其他)=默认值;
任务和运算符=(任务和其他)=默认值;
void运算符()()常量
{
std::cout一种方法是放弃lambda提供给您的语法糖,而是使用函子自己完成,例如:
#include <functional>
#include <iostream>
#include <string>
class Task
{
public:
Task()
{
}
Task(const Task& other) = delete;
Task& operator=(const Task& other) = delete;
Task(Task&& other) = default;
Task& operator=(Task&& other) = default;
void operator()() const
{
std::cout << "Task !" << std::endl;
}
};
class pseudo_lambda
{
public:
pseudo_lambda (Task &&task) { m_task = std::move (task); } // <- capture
void operator()() const { m_task (); } // <- invoke
private:
Task m_task; // <- captured variable(s)
};
int main()
{
auto task = Task();
pseudo_lambda pl { std::move (task) };
pl ();
}
#包括
#包括
#包括
课堂任务
{
公众:
任务()
{
}
任务(常量任务和其他)=删除;
任务和运算符=(常量任务和其他)=删除;
任务(任务和其他)=默认值;
任务和运算符=(任务和其他)=默认值;
void运算符()()常量
{
std::cout当您想要引用foo
的类型时,可以使用decltype(foo)
作为类型。因此,您可以执行以下操作:
decltype(lambda) test = std::move(lambda);
但是,您声明的目标是将其用作类成员。在这种情况下,您需要从中“窃取”类型。请注意,编译器没有义务(据我所知)统一两个相同lambda表达式的类型。这意味着类型和lambda创建都必须从同一lambda表达式获取
如果您确实希望使用lambdas执行此操作,并且您可以访问C++14(用于推导的返回类型),那么您可以执行以下操作:
auto make_task_runner(Task task) {
return [task = std::move(task)]() { task(); };
}
这为我们提供了一个函数,我们可以使用它创建lambda,也可以窃取类型(通过调用函数使用decltype()
)
然后,在你的课堂上,你可以:
class SomeClass {
// Type alias just to make things simpler.
using task_runner_t = decltype(make_task_runner(std::declval<Task>()));
task_runner_t task_runner;
}
但是,此时您已经失去了lambdas的主要优点:能够动态创建新的短期未命名函数。现在我们有了一个命名函数来创建lambda对象,并且我们为lambda类型指定了一个名称(任务运行器\u t
),那么使用lambda解决这个问题还有什么意义呢
在这种特殊情况下,自定义函子(如中)更有意义
…然而,Task
已经是一个functor,因此您已经拥有了所需的类型:Task
!只需使用它,而不是为了没有明显的好处而发明包装器。出于兴趣,如果您将其弹出到调试器或智能IDE中,它会说auto
将该类型解析为什么?可能是您需要的一些好信息可以使用。我是哑巴。这是一个问题,不是吗?打开了太多的危险标签:(@NathanOliver啊,完美。将其添加到重复列表中。@dgmz为了更直接地解决您的问题,每个lambda都有自己的匿名类型。但是,您可以使用decltype(lambda)test=std::move(lambda)引用该类型
。现在这是一个答案!感谢您的回答。即使在我的例子中,测试成员是类的一部分,而task runner将最终成为该类的构造函数模板(而不是该类)。但事实上,您正在回答这个确切的问题,我找到了一种方法,使用指向封装函子的类的唯一指针将函子存储在测试成员中,这样我现在存储函子就没有问题了!感谢您的回答。我理解您的解决方案,这允许我们自己确定类型。在我的不过,为我拥有的每种任务类型(不同的捕获参数等)创建一个伪_lambda类的成本相当高我会说是乏味的,而不是昂贵的。没有运行时开销。lambda只是我发布的代码的装饰。嗯,通常乏味意味着时间,不仅是我的时间,而且是其他人在我之后维护代码库的时间。既然时间就是金钱,至少对我的雇主来说,我支持我的选择;)
class SomeClass {
// Type alias just to make things simpler.
using task_runner_t = decltype(make_task_runner(std::declval<Task>()));
task_runner_t task_runner;
}
task_runner = make_task_runner(std::move(some_task));