如何修复Windows下std::map自定义分配器的编译错误?
自定义Allocator在VS2019编译失败的修复方案
问题背景
实现了一个自定义Allocator并用于std::map,但在Visual Studio 2019 x86环境下编译失败,原代码如下:
#include <iostream> #include <string> #include <vector> #include <map> #include <new> #include <climits> namespace test { template <class T> inline T* _allocate(ptrdiff_t size, T*) { std::cout << "_allocate called" << std::endl; T* tmp = (T*)(::operator new((size_t)(size * sizeof(T)))); if (NULL == tmp) { std::cerr << "out of memory" << std::endl; exit(0); } return tmp; } template <class T> inline void _deallocate(T* p) { ::operator delete(p); } template <class T1, class T2> inline void _construct(T1* p, const T2& value) { ::new (p) T1(value); } template <class T> inline void _destroy(T* p) { p->~T(); } template <class T> class Allocator { public: typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; template <class U> struct rebind { typedef Allocator<U> other; }; pointer allocate(size_type n, const void* hint = 0) { return _allocate((difference_type)n, (pointer)0); } void deallocate(pointer p, size_type n) { return _deallocate(p); } void construct(pointer p, const T& value) { _construct(p, value); } void destroy(pointer p) { _destroy(p); } pointer address(reference x) { return (pointer)&x; } const_pointer address(const_reference x) { return (const_pointer)&x; } size_type max_size() const { return size_type(UINT_MAX / sizeof(T)); } }; } // namespace test static std::map<void*, uint64_t, std::less<void*>, test::Allocator<std::pair<void* const, uint64_t>>> global_map; int main() { std::vector<std::string> vec = { "Hello", "from", "GCC", "!" }; std::cout << "xxxx " << global_map.size() << std::endl; }
编译错误信息
Build started... 1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------ 1>ConsoleApplication1.cpp 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1096,27): error C2440: 'static_cast': cannot convert from 'test::Allocator<U>' to 'test::Allocator<U>' 1> with 1> [ 1> U=std::_Tree_node<std::pair<void *const ,uint64_t>,void *> 1> ] 1> and 1> [ 1> U=std::_Container_proxy 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1096,27): message : No constructor could take the source type, or constructor overload resolution was ambiguous 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\xtree(1092): message : while compiling class template member function 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree(void) noexcept' 1> with 1> [ 1> _Kty=void *, 1> _Ty=uint64_t, 1> _Pr=std::less<void *>, 1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>> 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\map(348): message : see reference to function template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree(void) noexcept' being compiled 1> with 1> [ 1> _Kty=void *, 1> _Ty=uint64_t, 1> _Pr=std::less<void *>, 1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>> 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30037\include\map(75): message : see reference to class template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>' being compiled 1> with 1> [ 1> _Kty=void *, 1> _Ty=uint64_t, 1> _Pr=std::less<void *>, 1> _Alloc=test::Allocator<std::pair<void *const ,uint64_t>> 1> ]
修复方案
在自定义Allocator类中添加模板拷贝构造函数,修改后的Allocator类如下:
template <class T> class Allocator { public: typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; // 默认构造函数 Allocator() = default; // 模板拷贝构造函数:支持跨类型Allocator转换 template <class U> Allocator(const Allocator<U>&) noexcept {} template <class U> struct rebind { typedef Allocator<U> other; }; pointer allocate(size_type n, const void* hint = 0) { return _allocate((difference_type)n, (pointer)0); } void deallocate(pointer p, size_type n) { return _deallocate(p); } void construct(pointer p, const T& value) { _construct(p, value); } void destroy(pointer p) { _destroy(p); } pointer address(reference x) { return (pointer)&x; } const_pointer address(const_reference x) { return (const_pointer)&x; } size_type max_size() const { return size_type(UINT_MAX / sizeof(T)); } };
原理说明
- 错误原因:MSVC的STL实现中,
std::map这类容器会通过rebind生成不同类型的Allocator(比如用于内部节点、容器代理对象的Allocator),并需要将原Allocator实例转换为这些rebind后的Allocator类型。原代码中的Allocator没有提供跨类型的拷贝构造函数,编译器无法完成Allocator<_Tree_node<...>>到Allocator<_Container_proxy>的转换,导致编译报错。 - 修复原理:添加模板拷贝构造函数
template <class U> Allocator(const Allocator<U>&) noexcept {}后,允许任意类型的Allocator实例构造当前Allocator实例,满足了MSVC STL对Allocator之间可转换性的要求,解决了类型转换失败的问题。 - 显式声明默认构造函数是为了避免模板构造函数覆盖默认构造行为,确保
Allocator可以被无参构造。
内容的提问来源于stack exchange,提问作者heLomaN
相关产品推荐
相关产品推荐

