You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止std::pmr分配器向嵌套类型自动传播?

问题描述

需要创建一个包含其他PMR类型的PMR容器,但不希望内部类型使用与容器相同的分配器(内部类型需使用带有不同分配策略的内存资源),编写了如下代码:

#include <vector>
#include <string>
#include <memory_resource>

int main()
{
    std::pmr::polymorphic_allocator alloc_1;
    std::pmr::polymorphic_allocator alloc_2;
    
    std::pmr::vector<std::pmr::string> vec(alloc_1);

    vec.emplace_back("test1 this is a long string", alloc_2);
    vec.emplace_back("test2 this is a long string", alloc_2);
    
    return 0;
}

编译时出现错误:

/usr/include/c++/9/memory:212:22: error: static assertion failed: construction with an allocator must be possible if uses_allocator is true
  212 |        static_assert(is_constructible_v<_Tp, _Args..., const _Alloc&>,
      |                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

推测问题在于PMR分配器会自动将自身传播给容器元素的构造函数,导致调用std::pmr::string构造函数时传入了额外的分配器参数,使用C++20标准,询问是否有办法阻止PMR分配器向容器元素构造函数传播自身,或是否误解了错误。

问题原因

你的推测完全正确。std::pmr::vector作为PMR容器,其使用的std::pmr::polymorphic_allocator会触发分配器自动传播机制:当调用emplace_back在容器内直接构造元素时,容器会自动将自身的分配器(alloc_1)追加到你传入的参数列表末尾。

这就导致实际尝试构造std::pmr::string的参数是:("test1...", alloc_2, alloc_1),而std::pmr::string没有接受字符串、两个分配器的构造函数,因此触发了静态断言错误,检查是否能用这些参数构造元素。

解决方案

有两种简单有效的方法解决这个问题:

方法1:先构造元素再插入(push_back)

先使用目标分配器构造好std::pmr::string对象,再通过push_back(或移动插入)添加到容器。这种方式不会触发容器分配器的传播,因为元素已经构造完成:

#include <vector>
#include <string>
#include <memory_resource>

int main()
{
    std::pmr::polymorphic_allocator alloc_1;
    std::pmr::polymorphic_allocator alloc_2;
    
    std::pmr::vector<std::pmr::string> vec(alloc_1);

    // 先构造好使用alloc_2的string,再移动插入容器
    std::pmr::string s1("test1 this is a long string", alloc_2);
    vec.push_back(std::move(s1));
    
    std::pmr::string s2("test2 this is a long string", alloc_2);
    vec.push_back(std::move(s2));
    
    return 0;
}

方法2:使用std::piecewise_construct显式控制构造参数

通过std::piecewise_construct和tuple包装构造参数,让容器无法自动追加分配器参数。这种方式可以在容器内直接构造元素,避免额外的移动操作:

#include <vector>
#include <string>
#include <memory_resource>
#include <tuple>

int main()
{
    std::pmr::polymorphic_allocator alloc_1;
    std::pmr::polymorphic_allocator alloc_2;
    
    std::pmr::vector<std::pmr::string> vec(alloc_1);

    // 用piecewise_construct包装构造参数,阻止容器追加自身分配器
    vec.emplace_back(std::piecewise_construct,
                     std::make_tuple("test1 this is a long string"),
                     std::make_tuple(alloc_2));
                     
    vec.emplace_back(std::piecewise_construct,
                     std::make_tuple("test2 this is a long string"),
                     std::make_tuple(alloc_2));
    
    return 0;
}

补充说明

如果想彻底避免容器分配器的自动传播,也可以自定义一个包装分配器,让std::uses_allocator对目标元素类型返回false,但这种方式需要额外的模板代码,不如上面两种方法直接高效。

内容的提问来源于stack exchange,提问作者sycc90

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:05:18