霍华德·辛南特short_alloc的快速移动任务
Fast move assignment with Howard Hinnant's short_alloc
我正在使用Howard Hinnant漂亮的基于竞技场的小分配器,short_alloc。
令我震惊的是,从已经超出其竞技场并因此在堆上分配的向量中的移动分配可以使用通常的快速移动分配(即获取目标的资源(来完成。但是,情况并非如此:
typedef arena<16> arena_type;
typedef short_alloc<int, 16> alloc_type;
typedef std::vector<int, alloc_type> vec_type;
arena_type arena1, arena2;
vec_type vec1(alloc_type(arena1)), vec2(alloc_type(arena2));
vec1.resize(100);
void* data = vec1.data();
vec2 = std::move(vec1);
assert(vec2.data() == data); // fails
如本答案所述,这是由于向量的移动分配运算符比较了两个分配器(请注意,propagate_on_container_move_assignment
是std::false_type
(。由于两个分配器比较不相等(因为它们具有不同的竞技场(,因此目标向量需要分配内存并逐个移动值。
通过将相等运算符更改为
template <class T1, size_t N1, class T2, size_t N2>
bool operator==(const short_alloc<T1, N1>& x, const short_alloc<T2, N2>& y) noexcept
{
return N1 == N2 && (&x.a_ == &y.a_ || y.a_.on_heap());
}
其中on_heap()
检查分配器是否未使用其竞技场。
这个解决方案看起来相当笨拙(请注意,例如平等不是对称的(,我可以/会这样做在自己的脚上开枪吗?有没有优雅的解决方案?
两个不同的arena
对象可能具有不同的生存期。依赖于不同arena
对象的两个不同的short_alloc
对象管理具有不同生存期的内存。因此,具有不同short_alloc对象的两个std::vector
对象不能简单地在它们之间移动指针。
您的黑客将不起作用,因为它是从arena
或new[]
分配的指针。您的黑客假设分配器成为大向量的堆分配器,但事实并非如此。这不是分配器在不检查请求的大小或释放的指针的情况下知道的事情。
正确的解决方案是将分配器对象替换为移动运算符。为此,short_alloc
应定义:
using propagate_on_container_move_assignment = std::true_type;
private:
arena_type * a_; // <--- instead of reference
public:
short_alloc(const short_alloc&) = default;
// !!! Don't delete the move assignment !!!
// short_alloc& operator=(const short_alloc&) = delete;
这将使移动运算符按预期工作。移动后它将开始使用其他竞技场。
通常,这种分配技术非常危险,应该很少使用,因为它们与内存相关的错误风险很高。例如,如果要从函数返回向量,则它引用即将退出的作用域上的
arena
的风险很高。
随着我提议的改变,风险因素略高。arena
超出范围的问题现在也涉及到按引用传递向量。在内部块中定义arena
时,也存在arena
超出范围的问题。
其他arena
超出范围的这种行为可能会让程序员感到惊讶,并引入错误。这就是为什么我不喜欢这个解决方案。但是,有时人们愿意在时间关键部分(在分析和分析之后(编写危险的代码。
正如问题所建议的那样,可以在适用时将
short_alloc
分配器标记为堆分配器。可以在使用 new[]
的第一次分配之后以这种方式标记它。这将适用于std::vector
,因为它在方法调用之间只保留一个内存块。尽管可以使用 std::vector
,它与大多数其他容器中断,因为它们中的大多数都使用节点,例如 std::map
和 std::unordered_set
.
问题是一些节点来自arena
,一些来自堆。使用建议的operator==
,如果使用new[]
则返回true
,从std::map
移动将使来自不相关领域的某些节点移动目标std::map
。非常出乎意料,也是一个坏主意。这将导致一个 std::map
对象包含来自其自己的arena
和来自不相关arena
的节点。不相关的arena
节点永远不会被std::map
释放。只有当这些坏节点的分配arena
死亡时,这些坏节点才会被释放。
问题中提出的技术完全被打破了。它导致几乎任何事情的分配不一致,除了std::vector
。我强烈建议不要这样做。
- 将对象移动到std::shared_ptr
- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- 将shared_ptr移动到<StructA>shared_ptr<变体<结构A、结构 B>>
- C / C++ 移位/偏移/向左或向右移动位图?
- MSVC将仅移动结构参数解释为指针
- 自定义先决条件对移动分配运算符有效吗
- 返回值优化:显式移动还是隐式
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 为什么复制而不是移动数据元素?
- 可以使用移动语义更改或改进此C++代码吗?
- 使lambda不可复制/不可移动
- 有没有任务栏API可以立即应用注册表更改
- c++在使用指针时移动语义
- 将QGraphicsItem的移动区域限制在多边形区域内
- SendInput()鼠标移动计算
- 按值 C++ 返回时进行双倍移动
- 移动二维数组中的字符
- 将执行从一个线程移动到另一个线程,以实现任务并行性并在将来调用
- 霍华德·辛南特short_alloc的快速移动任务