当 astd::function被复制时,它引用的代码指令是否也被复制?
Anstd::function是通过某种形式的 callable 初始化的,它以某种方式指向可执行代码(就像函数指针通常所做的那样)。现在,当一个函数对象被复制时,这个可执行代码运行时是复制的还是内部引用的?重新表述这个问题:如果std::function复制了一个实例,那么内存中是否有多个相同编译代码指令的副本?是std::function实际存储函数代码的对象还是函数指针的抽象?
前者看起来很浪费,我不怀疑,但到目前为止我在这个主题上发现的一切要么太模糊,要么太具体,我无法肯定地说。例如
当目标是函数指针或 std::reference_wrapper 时,保证小对象优化,即这些目标总是直接存储在 std::function 对象内,不会发生动态分配。其他大对象可以在动态分配的存储中构造并由 std::function 对象通过指针访问。- cppreference
给出了一些关于它是如何完成的提示,但似乎仍然太模糊,并且可能与这个问题根本没有关系,因为std::function.
对于上下文:我正在尝试重构一些糟糕的 C 语言代码,这些代码将输入事件(击键、鼠标输入等)映射到特定行为,该行为在目标数据结构上执行,该数据结构可以被程序解释为更多具有除击键之外的语义上下文的特定输入(又名键绑定)。人们可以怀疑行为的要求变化很大。
这之前是通过定义和数字列表来实现的,这些列表指定了 input-event-ids,以及由 switch-case 选择的硬编码行为。我们很快接近了这种最初的做法变得笨拙的边界。
为了摆脱已定义的列表,实现可扩展的、声明性的、面向对象的和灵活的设计,我考虑了高阶函数。
特别是由于某些行为非常简单且需要重复使用(例如切换输出数据结构中的一个值),而其他行为在附加多个条件的情况下更加复杂,我想静态声明一些行为,但仍然会在某些情况下,喜欢只分配一些特殊的 lambda。由于我需要存储每个输入的行为(键、鼠标按钮、鼠标轴等),并且可能一次针对不同的键绑定集实例化一种特定行为类型的多个副本,我想知道是否应该引用这种行为,而不是按值存储。在前一种情况下,新的 lambdas 需要由行为结构拥有,但静态声明的行为不需要,这在实际中会导致一些shared_ptr恶作剧。在后一种情况下,根据价值,这不是问题,但我不希望例如切换行为的多个副本导致过多的冗余开销。
(注意:下面的整个讨论有点简化。AFAIK,没有错,但我确实省略了一些细节和边缘情况以及定义和实现的东西。)
该std::function不会不复制任何可执行代码。可执行代码始终仅由 指向std::function。当std::function被复制时,指针被复制(这完全没问题,因为可执行代码也永远不会被释放。)到目前为止,普通的旧函数指针和std::function.
但这还不是全部。
与函数指针相反, 的实例std::function可以携带“状态”以及指向可执行代码的指针,关于std::function必须分配/释放和复制/移动数据的整个喧嚣是关于这个额外的状态,而不是函数指针。
假设你有这样的代码:
(请注意,虽然我在这里使用了 lambda,但以下解释同样适用于 C++ 中的“函子”和“函数对象”和“绑定结果”以及其他形式的可调用事物,除了普通的旧函数指针.)
int x = 42, y = 17;
std::function<int()> f = [x, y] {return x + y;};
Run Code Online (Sandbox Code Playgroud)
在这里,f不仅存储指针为可执行代码return x + y;,但它也有记忆的价值x和y。由于您可以通过这种方式“捕获”的状态数量不受限制,因此 - 根据定义 -std::function必须在构造时从堆分配内存,并在适当的时间释放、复制和移动它。同样,复制的是这个额外的“状态”,而不是代码。
让我们回顾一下:每个都std::function需要能够存储至少一个指向可执行代码的指针,以及 0 个或更多字节的额外捕获状态。如果没有捕获状态, astd::function本质上与函数指针相同(尽管在实践中,std::functions 通常是多态实现的,并且在那里有其他东西。)
std::function我所知道的一些(大多数)实现采用了一种称为“小对象优化”的优化。在这些实现中,除了代码指针的空间外,std::function对象在其实例内部还有更多(固定数量的)空间(即作为其类的成员,而不是堆上的其他地方)并且将使用如果捕获状态的总字节数适合该区域。这消除了堆分配,这在某些用例中很重要,并且可以平衡使用的额外内存(当没有或几乎没有要捕获的状态时)。
| 归档时间: |
|
| 查看次数: |
92 次 |
| 最近记录: |