【发布时间】:2020-09-12 19:24:08
【问题描述】:
当std::function 被复制时,它引用的代码指令是否也被复制了?
std::function 是通过某种形式的可调用来初始化的,它以某种方式指向可执行代码(就像函数指针通常那样)。现在,当复制一个函数对象时,这个可执行代码运行时是复制还是内部引用?
换个说法:如果复制std::function 的一个实例,那么内存中是否存在相同编译代码指令的多个副本?
std::function 是实际存储函数代码的对象还是更多的是函数指针的抽象?
前者似乎很浪费,我不怀疑,但到目前为止我发现的关于这个主题的所有内容要么太模糊、缺乏,要么太具体,我无法肯定地说出来。例如
当目标是函数指针或 std::reference_wrapper 时,保证小对象优化,即这些目标总是直接存储在 std::function 对象中,不会发生动态分配。其他大对象可以在动态分配的存储中构造并由 std::function 对象通过指针访问。 - cppreference
给出了一些关于它是如何完成的提示,但似乎仍然太模糊,并且可能与这个问题完全无关,因为std::function 内部有进一步的抽象。
对于上下文:我正在尝试重构一些糟糕的 C-ish 代码,这些代码将输入事件(击键、鼠标输入等)映射到特定行为,该行为在目标数据上执行结构可以被程序解释为具有语义上下文而不是击键(也称为键绑定)的更具体的输入。人们可以怀疑行为的要求变化很大。
这以前是通过定义列表和指定输入事件 ID 的数字和硬编码行为来实现的,这些行为是由 switch-case 选择的。我们很快就接近了这种最初的做法变得笨拙的边界。
为了摆脱定义的列表,实现可扩展、声明性、面向对象和灵活的设计,我考虑使用高阶函数。
特别是由于某些行为非常简单且需要重复使用(例如,例如在输出数据结构中切换一个值),其他行为在附加多个条件的情况下更加复杂,我想静态声明一些行为,但仍然会在某些情况下,喜欢开放分配一些特殊的 lambda。由于我需要存储每个输入的行为(键、鼠标按钮、鼠标轴等),并且可能一次为不同的键绑定集实例化一种特定行为类型的许多副本,我想知道是否应该引用这种行为,而不是按价值存储。在前一种情况下,行为结构需要拥有新的 lambda,但静态声明的行为不需要,这实际上会导致一些 shared_ptr 恶作剧。在后一种情况下,从价值上看,这不是问题,但我不希望多个副本(例如切换行为)导致过多的冗余开销。
【问题讨论】:
-
std::function 是一个对象,就像任何 c++ 对象一样。复制向量时是否担心复制代码?
-
向量通常在缓冲区中没有可调用的指令,这些指令已被编译。
-
许多执行环境不支持动态代码生成(例如 ROM、许多手机应用商店)。即使那些支持它的人通常也不会提供足够的信息来复制和重新定位任意代码。 C++ 依赖编译器在编译时生成代码。实例数据当然是经常复制的。
-
“它引用的代码指令”是什么意思?可执行文件是在编译时生成的。在运行时,只有值被操纵和传递。我觉得这个基本概念中的某些东西在这里被遗漏了。
标签: c++ std-function