闭包(lambda 函数)的大小与 std::function 或指针不同

S.M*_*.M. 1 c++ c++14

#include <iostream>
#include <functional>

std::function<int(int)> makeLambda1(int x) {
  return [x] (int y) { return x * y; };
}

auto makeLambda2(int x) {
  return [x] (int y) { return x * y; };
}

auto makeLambda3() {
  return [] (int y) { return 10 * y; };
}

int main() {
  auto lambda1 = makeLambda1(10);
  auto lambda2 = makeLambda2(10);
  auto lambda3 = makeLambda3();
  std::cout << sizeof(lambda1) << " ";
  std::cout << sizeof(lambda2) << " ";
  std::cout << sizeof(lambda3) << " ";
  std::cout << sizeof(int(*)(int)) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

输出(https://ideone.com/ghoksF):

32 4 1 8
Run Code Online (Sandbox Code Playgroud)
  1. 为什么捕获 lambda 的大小为 4 字节并且小于 std::function 的大小(32 字节)?
  2. 为什么不捕获 lambda 的大小为 1 个字节并且小于函数指针的大小(8 个字节)?

Mil*_*nek 5

Lambda 只是重载对象的语法糖operator()。这意味着

return [x] (int y) { return x * y; };
Run Code Online (Sandbox Code Playgroud)

相当于

class unique_unnamed_type
{
private:
    const int x;
public:
    unique_unnamed_type(int x) : x{x} {}
    auto operator()(int y) { return x * y; } const
};
return unique_unnamed_type{x};
Run Code Online (Sandbox Code Playgroud)

仅从外观来看,您可能会猜测它sizeof(unique_unnamed_type)应该等于sizeof(int),因为它唯一的成员是一个int


在非捕获情况下,等效对象仅略有不同。

return [] (int y) { return 10 * y; };
Run Code Online (Sandbox Code Playgroud)

相当于

class unique_unnamed_type
{
private:
    using fp = int(*)(int);
    static auto unnamed_static_function(int y) { return 10 * y; }
public:
    auto operator()(int y) { return unnamed_static_function(y); } const
    operator fp() { return unnamed_static_function; } const
};
return unique_unnamed_type{};
Run Code Online (Sandbox Code Playgroud)

这种类型的大小通常为1,就像任何没有成员的类类型一样(对象需要有一个唯一的地址,因此即使是“空”对象也会占用一个字节的存储空间,除非诸如空基优化之类的事情)。

值得注意的是,它比函数指针小,因为它不是函数指针。它只是一个带有隐式转换运算符的类,该运算符返回指向静态成员函数的指针。


std::function是一个完全不同的野兽。它是任何具有兼容签名的可调用类型的类型擦除容器。这种类型擦除有一些开销,因此std::function通常会比原始函数指针或 lambda 更大,但如果 lambda 捕获一个大对象,它可能会更小。对象引用的实际可调用std::function对象将动态分配,因此sizeof(std::function<int(int)>)不包括实际可调用对象的大小。