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));