You need to enable JavaScript to run this app. C++捕获/非捕获lambda与std::function、函数指针的sizeof差异解析
优惠活动
大模型
产品
解决方案
定价
更多

C++捕获/非捕获lambda与std::function、函数指针的sizeof差异解析

C++可调用对象的内存大小差异解析

测试代码

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

运行输出

32 4 1 8

核心原理说明

1. 捕获式lambda体积远小于对应std::function的原因

  • lambda的本质:编译器会为每段lambda代码生成一个唯一的匿名闭包类,lambda表达式产生的对象就是这个类的实例。类的成员变量完全对应lambda捕获的外部变量,没有冗余结构。本次测试中捕获的是单个int类型变量,刚好占4字节,因此lambda实例的大小就是4字节,没有额外开销。
  • std::function的本质:它是支持所有可调用对象(普通函数、lambda、仿函数、成员函数指针等)的通用类型擦除封装器,设计目标是一套接口兼容所有可调用类型,因此必然存在固定的结构开销:
    • 预留固定大小的内部缓冲区,用来直接存储体积较小的可调用对象,避免堆分配开销
    • 存储一组类型擦除的操作函数指针,负责完成内部可调用对象的调用、拷贝、销毁逻辑
    • 维护自身的状态标记,判断内部存储的对象类型
      这些固定结构的体积叠加后,哪怕内部存储的是4字节的小lambda,整个std::function实例的大小也会达到32字节,这是通用适配能力带来的固有体积代价。

2. 非捕获式lambda体积小于普通函数指针的原因

  • 非捕获式lambda没有任何需要捕获的外部变量,对应的匿名闭包类没有任何成员变量,属于C中的空类。按照C内存规则,空类的大小不能为0——否则无法保证不同类实例的内存地址唯一,因此编译器会给空类自动加上1字节的占位,这就是其大小为1字节的来源。
  • 64位系统下的普通函数指针,本身需要存储函数入口的虚拟地址,64位地址长度恰好为8字节,因此同签名的普通函数指针大小为8字节。需要特别注意的是:非捕获lambda本身的1字节只是空类的占位,内部并没有存储函数地址;只有当非捕获lambda被隐式转换为普通函数指针时,才会生成一个8字节的指针值,指向编译器为这个lambda生成的静态函数实体。

内容的提问来源于stack exchange,提问作者3CEZVQ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.28 13:45:00