自定义分配器适配std::unordered_map编译错误解决咨询
自定义分配器适配STL容器的正确姿势
先解决「too many initializers」的常见诱因
这个错误大概率和分配器的构造逻辑不匹配STL要求有关:
- 你的
MyMem分配器可能缺少模板转换构造函数——STL容器内部会在不同类型(比如容器节点类型和元素类型)的分配器之间转换,没有这个构造函数会导致初始化时参数不匹配。 - 自定义字符串
lstr的分配器绑定或初始化有误,比如声明时没指定正确的分配器类型,或者初始化时多传了参数。
STL分配器必须满足的核心规则
C++标准对分配器有明确的概念要求,核心要点不能少:
- 必须定义嵌套类型:
value_type:分配的对象类型size_type:无符号整数,用于表示内存大小difference_type:有符号整数,用于指针差值计算
(pointer/const_pointer可选,但建议定义)
- 必备构造函数:
- 默认构造函数
- 拷贝构造函数
- 模板转换构造函数(适配不同
value_type的分配器,比如容器节点分配时需要):template <typename U> MyMem(const MyMem<U>&) noexcept {}
- 核心内存操作函数:
// 分配内存,要做溢出检查 T* allocate(size_type n) { if (n > std::numeric_limits<size_type>::max() / sizeof(T)) { throw std::bad_alloc(); } return static_cast<T*>(std::malloc(n * sizeof(T))); } // 释放内存,不需要大小参数(但要保留参数位符合标准) void deallocate(T* p, size_type) noexcept { std::free(p); } - 相等性比较:无状态分配器直接返回相等即可,有状态则按需实现:
template <typename T, typename U> bool operator==(const MyMem<T>&, const MyMem<U>&) noexcept { return true; } template <typename T, typename U> bool operator!=(const MyMem<T>&, const MyMem<U>&) noexcept { return false; }
针对你的场景的适配示例
1. 正确实现MyMem分配器
#include <cstdlib> #include <memory> #include <unordered_map> #include <string> #include <limits> template <typename T> class MyMem { public: using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; // 默认构造、拷贝构造用默认实现 MyMem() noexcept = default; MyMem(const MyMem&) noexcept = default; // 模板转换构造函数,适配不同类型的分配器 template <typename U> MyMem(const MyMem<U>&) noexcept {} // 分配内存 pointer allocate(size_type n) { if (n > std::numeric_limits<size_type>::max() / sizeof(T)) { throw std::bad_alloc(); } return static_cast<pointer>(std::malloc(n * sizeof(T))); } // 释放内存 void deallocate(pointer p, size_type) noexcept { std::free(p); } // 可选:构造/销毁对象(C++17后可省略,编译器会用默认实现) template <typename U, typename... Args> void construct(U* p, Args&&... args) { ::new(p) U(std::forward<Args>(args)...); } template <typename U> void destroy(U* p) { p->~U(); } }; // 分配器相等性比较 template <typename T, typename U> bool operator==(const MyMem<T>&, const MyMem<U>&) noexcept { return true; } template <typename T, typename U> bool operator!=(const MyMem<T>&, const MyMem<U>&) noexcept { return false; }
2. 自定义字符串lstr的正确写法
如果是基于std::basic_string封装,直接绑定分配器即可:
template <typename CharT> using lstr = std::basic_string<CharT, std::char_traits<CharT>, MyMem<CharT>>;
如果是完全自定义的字符串类,要在内部正确使用分配器:
class lstr { private: using allocator_type = MyMem<char>; allocator_type alloc_; char* data_ = nullptr; size_t size_ = 0; size_t capacity_ = 0; public: lstr() = default; explicit lstr(size_t n, char c = '\0') { size_ = n; capacity_ = n; data_ = alloc_.allocate(n); for (size_t i = 0; i < n; ++i) { alloc_.construct(data_ + i, c); } } // 析构时要销毁对象再释放内存 ~lstr() { for (size_t i = 0; i < size_; ++i) { alloc_.destroy(data_ + i); } if (data_) { alloc_.deallocate(data_, capacity_); } } // 禁用拷贝(如果需要拷贝要自己实现深拷贝逻辑) lstr(const lstr&) = delete; lstr& operator=(const lstr&) = delete; // 移动构造/赋值可以按需实现 lstr(lstr&& other) noexcept : alloc_(std::move(other.alloc_)), data_(other.data_), size_(other.size_), capacity_(other.capacity_) { other.data_ = nullptr; other.size_ = 0; other.capacity_ = 0; } lstr& operator=(lstr&& other) noexcept { if (this != &other) { // 先清理当前资源 for (size_t i = 0; i < size_; ++i) { alloc_.destroy(data_ + i); } if (data_) { alloc_.deallocate(data_, capacity_); } // 转移资源 data_ = other.data_; size_ = other.size_; capacity_ = other.capacity_; other.data_ = nullptr; other.size_ = 0; other.capacity_ = 0; } return *this; } };
3. std::unordered_map的正确使用
unordered_map的分配器是针对内部节点类型(std::pair<const Key, Value>)的,所以要指定对应类型的分配器:
// 定义使用MyMem的unordered_map,键是lstr<char>,值是int using MyMap = std::unordered_map< lstr<char>, int, std::hash<lstr<char>>, std::equal_to<lstr<char>>, MyMem<std::pair<const lstr<char>, int>> >; // 初始化使用示例 MyMap my_map; my_map["hello"] = 456;
排查错误的具体步骤
- 检查
MyMem是否有模板转换构造函数:如果没有,容器内部转换分配器类型时会触发初始化参数错误。 - 检查
lstr的初始化代码:比如构造函数参数数量是否匹配,有没有多传分配器相关参数。 - 检查unordered_map的模板参数:确保分配器是针对
std::pair<const Key, Value>的,而不是Key或Value类型的分配器。
内容的提问来源于stack exchange,提问作者user2961927
相关产品推荐
相关产品推荐

