如何确定捕获一个不可复制参数的lambda的类型?

问题描述 投票:1回答:1

给定不可复制的任务类和下面的示例代码。

#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();
}

如果我宣布 检验 类型的变量 自动 而不是 程式我的程序在编译后能完美地运行,否则就会出现这个错误而拒绝编译。

functional:1878:34: error: use of deleted function 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
    __dest._M_access<_Functor*>() =
                                  ^
31:42: note: 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)' is implicitly deleted because the default definition would be ill-formed:
31:42: error: use of deleted function 'Task::Task(const Task&)'
13:5: note: declared here

我真的需要声明测试的类型 因为它最终会成为另一个类的成员.

我该怎么做呢?

我认为std::function应该以某种方式声明为可突变的,这样做对吗?

c++ lambda move noncopyable
1个回答
4
投票

你可以使用 decltype(foo) 作为一个类型,当你想引用类型的 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(); };
}

这给了我们一个函数,我们既可以用来创建lambdas,也可以用来偷取类型(通过使用 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;
}

然后你就可以通过使用函数的 make_task_runner 函数。

task_runner = make_task_runner(std::move(some_task));

然而,在这一点上,你已经失去了lambdas的主要好处: 能够在运行中创建一个新的短命的,未命名的函数。 现在我们有了一个命名的函数来创建lambda对象,并且我们已经给lambda类型起了一个名字(task_runner_t),那么使用lambda来解决这个问题还有什么意义呢?

在这种特殊情况下,一个自定义的漏斗(如在 保罗的回答)就更有意义了。

... 但是 Task 已经是一个漏斗 所以你已经有了你需要的类型。Task! 就用它,而不是发明一个包装器,这样做没有明显的好处。


2
投票

一种方法是放弃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 ();
}

现场演示

© www.soinside.com 2019 - 2024. All rights reserved.