为什么 lambda 的大小为 1 个字节?

2024-03-09

我正在使用 C++ 中的一些 lambda 表达式的内存,但我对它们的大小有点困惑。

这是我的测试代码:

#include <iostream>
#include <string>

int main()
{
  auto f = [](){ return 17; };
  std::cout << f() << std::endl;
  std::cout << &f << std::endl;
  std::cout << sizeof(f) << std::endl;
}

输出是:

17
0x7d90ba8f626f
1

这表明我的 lambda 的大小是 1。

  • 这怎么可能?

  • lambda 不应该至少是一个指向其实现的指针吗?


所讨论的 lambda 实际上有no state.

Examine:

struct lambda {
  auto operator()() const { return 17; }
};

如果我们有lambda f;,这是一个空类。不仅是以上这些lambda功能上与您的 lambda 类似,它(基本上)是您的 lambda 的实现方式! (它还需要隐式转换为函数指针运算符,并且名称lambda将被一些编译器生成的伪 GUID 替换)

在 C++ 中,对象不是指针。它们是真实的东西。它们仅占用存储数据所需的空间。指向对象的指针可以比对象大。

虽然您可能认为 lambda 是指向函数的指针,但事实并非如此。您不能重新分配auto f = [](){ return 17; };到不同的函数或 lambda!

 auto f = [](){ return 17; };
 f = [](){ return -42; };

以上是illegal。没有房间了f储藏which函数将被调用——该信息存储在type of f,不在值中f!

如果你这样做:

int(*f)() = [](){ return 17; };

or this:

std::function<int()> f = [](){ return 17; };

您不再直接存储 lambda。在这两种情况下,f = [](){ return -42; }是合法的——所以在这些情况下,我们正在存储which我们正在调用的函数的值是f. And sizeof(f)不再是1, 反而sizeof(int(*)())或更大(基本上,如您所期望的,是指针大小或更大。std::function具有标准隐含的最小大小(它们必须能够在“内部”存储达到一定大小的可调用对象),实际上至少与函数指针一样大)。

In the int(*f)()在这种情况下,您正在存储一个指向函数的函数指针,该函数的行为就像您调用该 lambda 一样。这仅适用于无状态 lambda(带有空的 lambda)[]捕获列表)。

In the std::function<int()> f情况下,您正在创建一个类型擦除类std::function<int()>实例(在本例中)使用placement new 将 size-1 lambda 的副本存储在内部缓冲区中(并且,如果传入更大的 lambda(具有更多状态),将使用堆分配)。

作为猜测,您可能认为正在发生类似的事情。 lambda 是一个对象,其类型由其签名描述。在 C++ 中,决定使用 lambda零成本对手动函数对象实现的抽象。这可以让你将 lambda 传递到std算法(或类似的),并在实例化算法模板时使其内容对编译器完全可见。如果 lambda 有这样的类型std::function<void(int)>,它的内容不会完全可见,并且手工制作的函数对象可能会更快。

C++ 标准化的目标是对手工编写的 C 代码进行零开销的高级编程。

现在你明白了你的f事实上是无状态的,你脑子里应该还有另一个问题:lambda 没有状态。为什么没有尺寸0?


有一个简短的答案。

标准下C++中的所有对象的最小大小必须为1,并且同一类型的两个对象不能具有相同的地址。这些是相连的,因为类型数组T将放置元素sizeof(T) apart.

现在,由于它没有状态,有时它可以不占用任何空间。当它“单独”时这不会发生,但在某些情况下它可能会发生。std::tuple类似的库代码利用了这一事实。下面是它的工作原理:

因为 lambda 相当于一个类operator()重载的、无状态的 lambda(带有[]捕获列表)都是空类。他们有sizeof of 1。事实上,如果您继承它们(这是允许的!),它们将不占用任何空间只要不引起同类型地址冲突。 (这称为空基优化)。

template<class T>
struct toy:T {
  toy(toy const&)=default;
  toy(toy &&)=default;
  toy(T const&t):T(t) {}
  toy(T &&t):T(std::move(t)) {}
  int state = 0;
};

template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }

the sizeof(make_toy( []{std::cout << "hello world!\n"; } )) is sizeof(int)(好吧,上面是非法的,因为你不能在非评估上下文中创建 lambda:你必须创建一个命名的auto toy = make_toy(blah);然后做sizeof(blah),但这只是噪音)。sizeof([]{std::cout << "hello world!\n"; })还是1(类似资格)。

如果我们创建另一种玩具类型:

template<class T>
struct toy2:T {
  toy2(toy2 const&)=default;
  toy2(T const&t):T(t), t2(t) {}
  T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }

这有两份的 lambda 。由于他们不能共用同一个地址,sizeof(toy2(some_lambda)) is 2!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 lambda 的大小为 1 个字节? 的相关文章

随机推荐

  • 如何在 gitlab CE 9 中将项目从一个组转移到一个用户?

    我想将项目从一个组转移到另一个用户 例如 来自https gitlab local groupname projectname https gitlab local groupname projectname to https gitlab
  • 如何在 C 或 C++ 中获取/链接外部函数?

    EDIT 我想我应该澄清一下 以防万一 我使用的是 AIX Unix 机器 所以我使用 VAC 编译器 没有 gnu 编译器 End edit 我对 C C 很生疏 所以如果这是一个简单的问题 请原谅我 我想从我的一些 C 程序中取出常用函
  • helgrind (valgrind) 可以与 c++11 futures 一起使用吗

    当我将 helgrind 与 C 11 futures 和打包任务一起使用时 我得到了我认为是误报的信息 以下是在 CentOS6 系统上使用 gcc 6 3 0 和 valgrind 3 12 的情况 我尝试按照文档中的建议来提供注释 我
  • css盒子阴影+透明背景图片=直观分解

    我有一个按钮图像用作某些链接的背景图像 背景图像有圆角 我想使用 css 投影而不是将投影放在图像中 问题是 阴影似乎是绘制的around元素 虽然我有点希望通过背景图像的透明部分看到投影颜色 但我看到的是背景颜色 看到这个jsfiddle
  • 如何更改 BigQuery 重复记录的 col 类型

    我正在尝试更改重复记录的 col 类型STRING to TIMESTAMP BQ 文档提供了一些建议 手动更改模式 https cloud google com bigquery docs manually changing schema
  • UIPageViewController:子控制器在滚动期间忽略状态栏高度

    我遇到了一些麻烦UIPageViewController 如果我滚动到新页面 新的视图控制器是behind状态栏while我正在滚动 滚动后 视图控制器自行定位below状态栏 我正在使用故事板 通用 UIPageViewControlle
  • 有没有一种有效的方法来引用常量,而不是只读?

    我们来看下面的 C 代码 include
  • 与网络服务器同步核心数据

    我正在创建一个关于鸟类的应用程序 这个应用程序也应该在离线模式下工作 所以我决定使用核心数据 我计划有 1 个表 其中包含大约 700 条记录 所有鸟类都将显示在表格视图中 工作流程应该是这样的 用户启动应用程序 请求在后台发送到服务器 响
  • 如何使用批量更改壁纸

    我需要制作一个批处理文件 将壁纸更改为与bat文件位于同一位置的图片 我当前有以下代码 reg add HKCU Control Panel Desktop v Wallpaper f t REG SZ d c images wallpap
  • 哪一个更快? List.contains() 或 Map.containsKey()

    我正在编写一个算法 在其中寻找成对的值 这些值加在一起时会产生我正在寻找的另一个值 我发现使用Map将使我的算法速度从 O n 开始 后来我意识到我并没有真正使用我的Map so a List就足够了 我在谷歌上进行了强力搜索 但在我的问题
  • 如何在Sqlite3中将数组存储在一列中?

    有没有办法将整数数组存储在表的一列中 我想要这样的o p ident value count 563 0 10 0 0 1 100 2 200 3 300 4 400 5 500 6 我已经通过 postgres 实现了这
  • 为什么 SSL 握手会出现“无法生成 DH 密钥对”异常?

    当我与某些 IRC 服务器 但不是其他服务器 可能是由于服务器的首选加密方法 建立 SSL 连接时 出现以下异常 Caused by java lang RuntimeException Could not generate DH keyp
  • 使用 CSS 为黑色图标赋予另一种颜色

    我看到一些应用程序尽管包含黑色图标 但有些应用程序如何使用 CSS 将图标转换为不同的颜色 我似乎无法重复这个过程 这是我的 back css 文件 dashboard buttons a width 80px height 80px bo
  • Solr 搜索查询返回全头异常

    我正在 C 应用程序中调用托管在其他计算机上的远程 solr 搜索 现在 由于我的查询长度变得太大 因此搜索引擎返回全头错误 我无法减少查询长度 所以我只是想知道我可以提出同样的邮寄请求吗 我该怎么做这个 请建议我 谢谢 看起来您遇到了 j
  • 在回调中使用yield?

    我有一个函数y 这应该会产生一些记录 然而 该函数在回调中获取记录 并将其传递给另一个函数d 访问数据 d 不返回或产生任何东西 如果其他功能 这种模式是否可能d 接受回调被认为是黑匣子 替代设计是什么 function y d funct
  • 单元测试、NUnit 还是 Visual Studio? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我正在使用 Visual studio 有时是 resharper 来运行我的单元测试 我听说过 NUnit 但我对它了解不多 我应该关心它吗
  • 在 kubernetes pod 中获取日志的选项

    kubernetes pod 中的开发人员日志很少 获取日志供开发人员查看的最佳方法是什么 我们可以使用什么特定的工具 我可以选择graylog 但不确定是否可以对其进行定制以使开发人员登录到其中 最基本的方法是简单地使用kubectl l
  • Python:连接(或克隆)一个 numpy 数组 N 次

    我想通过克隆 Mx1 ndarray N 次来创建 MxN numpy 数组 有没有一种有效的 pythonic 方法来代替循环 顺便说一句 以下方法对我不起作用 X 是我的 Mx1 数组 numpy concatenate X numpy
  • 文件读取:feof() 用于二进制文件

    我正在读取一个二进制文件 当它到达终点时 看来它是由 feof 函数终止的 是因为二进制文件没有EOF字符吗 如果是这样我该如何解决它 目前我的代码正在使用 while 循环 while feof f 当它到达文件末尾位置 5526900
  • 为什么 lambda 的大小为 1 个字节?

    我正在使用 C 中的一些 lambda 表达式的内存 但我对它们的大小有点困惑 这是我的测试代码 include