应用移动 lambda 闭包

4

这可能是一个概念性问题。我正在实现接受 lambda 作为参数的函数。然而,我无法理解 lambda 的确切类型。例如:

auto T = [] () { printf("hello world\n"); };
auto F = move(T);
T();  // print "hello world"
F();  // print "hello world"

我以为在调用Tmove方法之后,T的内容会消失。换句话说,我期望有如下行为:

function<void> T = [] () { printf("hello world\n");};
auto F = move(F);
F();  // print "hello world"
T();  // throw error

回到最初的问题,如何最佳实践地传递/赋值一个lambdafunction<void()>类成员?我看到了许多不同的答案,一些使用const function<void()>&,另外一些则建议使用模板F&&
struct Foo {

  function<void()> f;

  // Option 1:
  void set_f(const function<void()>& in) {f=in;}

  // Option 2: template
  template <typename F>
  void set_f(F&& in) { // what to write here??? }  
}

这两个选项是否足够通用以涵盖大多数输入类型?
1个回答

6

您似乎对编译器如何处理lambda表达式有基本的误解。Lambda表达式会被转换成具有唯一名称的函数对象。当您调用lambda时,实际上是在调用该函数对象的operator()方法。

因此,在您的第一个示例中,lambda将创建类似于以下内容的内容:

struct __uniquely_named_lambda
{
    void operator()() const
    {
        printf("hello world\n");
    }
};

如果这个 lambda 表达式存储了任何状态,那么从中进行 move 操作将移动该状态,但是您的 lambda 表达式是无状态的,因此 move 不起作用。您不能剥离 operator() 的主体并将其移动到其他地方。
例如,这些语句将生成输出 4 0 4。
std::string s{"Test"};
auto T = [s]() { std::cout << s.size() << ' '; };  // make a copy of s
T();
auto F = std::move(T);
T();
F();

演示链接


std::function是一个容器,可以接受任何匹配指定签名的可调用对象,而lambda表达式就是这样的一个可调用对象。当您移动std::function时,实际上是将其存储的可调用目标移动到目标位置。在原始位置尝试调用该目标将会抛出bad_function_call异常,这与移动lambda表达式非常不同。


我会如下编写set_f成员函数

template <typename F>
void set_f(F&& in)
{
    f = std::forward<F>(in);
}

F在你的例子中是一个转发引用,这意味着它可以接受调用者传递的左值或右值。然后赋值将会复制赋值或移动赋值参数。


1
@dau_sama,就f被分配参数而言,它可以工作,但它可能不会做调用者期望的事情。如果您使用非“const”左值引用调用set_fstd::function,它仍将“move”参数,这可能不是您想要的。如果您想始终“move”,则应该通过值获取F - Praetorian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接