如何确定捕获不可复制参数的 lambda 的类型?
How to determine the type of a lambda which captures a noncopyable parameter?
给定不可复制的任务 class 和下面的示例代码
#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();
}
如果我声明 test 变量类型为 auto 而不是 std::function,程序完美编译运行,否则会拒绝编译,报错:
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
我真的需要声明测试类型,因为它最终将成为另一个 class 的成员。
我该怎么做?
我认为 std::function 应该以某种方式声明为可变的吗?
解决这个问题的一种方法是放弃 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 ();
}
当你想引用foo
的类型时,你可以使用decltype(foo)
作为类型。所以,你可以这样做:
decltype(lambda) test = std::move(lambda);
但是,您声明的目标是将其用作 class 会员。在那种情况下,您需要 "steal" 来自的类型。请注意,编译器没有义务(据我所知)统一两个相同 lambda 表达式的类型。这意味着必须从同一个 lambda 表达式中获取类型和 lambda 创建。
如果你真的想用 lambdas and 来做到这一点,你可以访问 C++14(对于推导的 return 类型),那么你可以这样做:
auto make_task_runner(Task task) {
return [task = std::move(task)]() { task(); };
}
这给了我们一个函数,我们可以使用它来创建 lambda 和窃取类型(通过使用 decltype()
调用函数)。
然后,在您的 class 中,您可以:
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));
但是,此时您已经失去了 lambda 的主要好处:即时创建新的短期未命名函数的能力。现在我们有一个命名函数来创建 lambda 对象,并且我们已经为 lambda 类型指定了一个名称 (task_runner_t
),那么使用 lambda 来解决这个问题还有什么意义呢?
在这种特殊情况下,自定义仿函数(如 )更有意义。
... 然而,Task
已经是一个仿函数 所以你已经有了你需要的类型:Task
!只需使用它而不是发明一个没有明显好处的包装器。
给定不可复制的任务 class 和下面的示例代码
#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();
}
如果我声明 test 变量类型为 auto 而不是 std::function,程序完美编译运行,否则会拒绝编译,报错:
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
我真的需要声明测试类型,因为它最终将成为另一个 class 的成员。
我该怎么做?
我认为 std::function 应该以某种方式声明为可变的吗?
解决这个问题的一种方法是放弃 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 ();
}
当你想引用foo
的类型时,你可以使用decltype(foo)
作为类型。所以,你可以这样做:
decltype(lambda) test = std::move(lambda);
但是,您声明的目标是将其用作 class 会员。在那种情况下,您需要 "steal" 来自的类型。请注意,编译器没有义务(据我所知)统一两个相同 lambda 表达式的类型。这意味着必须从同一个 lambda 表达式中获取类型和 lambda 创建。
如果你真的想用 lambdas and 来做到这一点,你可以访问 C++14(对于推导的 return 类型),那么你可以这样做:
auto make_task_runner(Task task) {
return [task = std::move(task)]() { task(); };
}
这给了我们一个函数,我们可以使用它来创建 lambda 和窃取类型(通过使用 decltype()
调用函数)。
然后,在您的 class 中,您可以:
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));
但是,此时您已经失去了 lambda 的主要好处:即时创建新的短期未命名函数的能力。现在我们有一个命名函数来创建 lambda 对象,并且我们已经为 lambda 类型指定了一个名称 (task_runner_t
),那么使用 lambda 来解决这个问题还有什么意义呢?
在这种特殊情况下,自定义仿函数(如
... 然而,Task
已经是一个仿函数 所以你已经有了你需要的类型:Task
!只需使用它而不是发明一个没有明显好处的包装器。