C++ 中的 Lambda <-> 闭包等价

Lambda <-> closure equivalence in C++

我正在观看一个名为 Lambda? You Keep Using that Letter 的视频,作者是 Kevlin Henney,他在视频中指出闭包和对象在根本上是等价的:

然后他通过 this javascript code 证明了他的观点,将堆栈实现为闭包:

const newStack = () => {
    const items = []
    return {
        depth: () => items.lengh,
        top: () => items[0],
        push: newTop => { items.unshift(newTop) },
        pop: () => { items.shift() },
    }
}

闭包与 class 相比的优势在于它的状态确实是隐藏的,而私有成员 "inaccessible" 多于 "hidden"。

我试图在 C++ 中做一些等效的事情。然而,似乎很难用C++表达这一点。

我现在的版本在那里,它有两个主要缺点:

auto newStack = []() {
  auto items = std::make_shared<std::stack<int>>();

  auto depth = [&items]() { return items->size();};
  auto top = [&items]() { return items->top(); };
  auto push = [&items](int newTop) {  items->push(newTop); };
  auto pop = [&items]() { items->pop(); };

  struct R {
    decltype(depth) depth;
    decltype(top) top;
    decltype(push) push;
    decltype(pop) pop;
  };
  return R{ depth, top, push, pop};
};

godbolt version here

在 C++ 中有可行的方法吗?

是的,在 C++ 中当然有更好的方法:不要使用 lambda。

一个 lambda 表达式定义了一个 class。闭包是 class——一个对象的一个​​实例。我们不需要与其他语言进行比较就可以告诉我们这一点——这正是 C++ 中定义 lambda 和闭包的方式。 §[expr.prim.lambda.closure]:

The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

但是(这是重要的一点)至少在 C++ 中,lambda 表达式定义了一个 class 和一个 非常 有限的 public 接口。具体来说,它提供 operator() 的重载,如果它没有捕获任何内容,则转换为指向函数的指针。如果它确实捕获了某些东西,它还会定义一个构造函数来进行捕获。当然,如果它捕获了东西,它会定义成员变量来保存它捕获的任何东西。

但这就是它真正定义的全部。这并不是说它在隐藏它可能包含的任何其他内容方面做得更好。就是它真的没有包含任何其他内容。

在你的例子中,你试图定义一个类型,它有四个独立的成员函数,它们都在它们共享的某个状态下运行。正如您所展示的那样,将状态外部化是有可能的,因此您拥有的东西几乎等同于一些 C 代码(或按该顺序的东西),它只包含数据和一些对该数据进行操作的函数。是的,您可以将它们组合到一个结构中,至少可以对具有成员函数的 class 进行一些模仿。

但是您几乎是在与系统(可以这么说)作斗争以在 C++ 中执行此操作。 Lambdas/closures(因为它们是在 C++ 中定义的)并不是为了让您定义具有多个独立入口点的事物,每个入口点对共享数据执行单独的操作。正如塞缪尔约翰逊的老话所说,“[它]就像一只用后腿走路的狗。它做得不好;但你会惊讶地发现它完全完成了。”