gpt4 book ai didi

c++ - 为什么位置不变性对 std::function 很重要?

转载 作者:行者123 更新时间:2023-12-04 13:05:14 26 4
gpt4 key购买 nike

我一直在查看 std::function 的 GCC 实现(在调试时到达那里并在切线上离开)。
从我所看到的,它在本地存储中存储了小类型,任何不适合它的东西都通过 new 操作符分配。
然而,构造函数也会检查 __location_invariant元函数,它是 std::trivially_copyable 的包装器trait,如果它不是“位置不变”,它也会在堆上分配它。
我不完全理解它为什么这样做,正如我所理解的::new (storage) T(args)应该提供相同的结果new T(args)除了就地构造函数不分配任何内存。
例如,如果它使用单个引用计数对象来存储太大而无法放入本地存储的“位置不变”类型,那对我来说会更有意义,因为这会减少分配和复制的数量。由于每次都分配和复制非不可变对象(immutable对象),因为它们是“位置相关的”,它们不能都引用相同的存储。
实现似乎只是堆分配任何不适合和/或不是位置不变的东西(至少我没有看到它这样做?),所以我很困惑为什么它需要检查位置不变性,如果功能上没有明显区别。

最佳答案

似乎 libstdc++ 使用“位置不变”属性来简化 std::function 上的某些操作。 .即,可调用对象的存储由 union named _Any_data 提供。其中包含一个字符数组。这个 char 数组可以为指向实际可调用对象的指针(如果它在堆上分配)或可调用对象本身(如果它有资格进行小对象优化)提供存储。当std::function是移动构造的,_Any_data RHS 的成员只需简单地复制到 _Any_data *this成员(member)(加上 RHS 必须以某种方式表示为空)。这在 _Any_data 时都有效存储一个指向堆分配的可调用对象的指针(因为该指针是可简单复制的),并且当它存储一个小的内联可调用对象时(因为在这种情况下需要可调用对象是可简单复制的)。类似的交换操作 std::function可以作为 _Any_data 的简单交换来实现成员和复制/移动赋值操作都是使用复制交换习语实现的。
可能会更慷慨一些:理论上可以支持任何可调用类型,无论是 nothrow-copy-constructible 还是 nothrow-move-constructible,都可以支持小对象优化。 [1] 然而,在类型不可简单复制的情况下,这会给实现带来额外的复杂性。考虑如何写std::function的移动构造函数以防 RHS 可能会内联存储一个不能简单复制的对象。在这种情况下,必须根据存储的元数据是否表明此类构造函数是非平凡的,有条件地调用此类可调用对象的复制构造函数。这并不难实现:只需将一个额外的方法添加到管理器对象中即可。但是,这意味着每次 std::function 都必须执行额外的间接函数调用。是移动构造的。在交换操作的情况下,需要 3 次这样的调用。
实现者需要做出的权衡是,适合小对象缓冲区且不可抛出移动(但不能简单复制)的类型是否足够普遍,以至于允许它们存储在小对象缓冲区中的好处超过所有可调用类型的移动和交换操作使用的额外间接函数调用的成本。
[1] 这个要求是必要的原因(或至少一个原因)是交换两个 std::function对象必须始终成功。交换操作实际上必须重新定位任何内联存储的值(与堆上的值相反,在这种情况下,指针的所有权可能会简单地转移到另一个 std::function 对象)。如果此类重定位中涉及的底层复制或移动不是 noexcept ,则无法保证交换会成功;因此堆分配是唯一的选择。

关于c++ - 为什么位置不变性对 std::function 很重要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69845195/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com