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

如何修复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)); }
};

原理说明

  1. 错误原因:MSVC的STL实现中,std::map这类容器会通过rebind生成不同类型的Allocator(比如用于内部节点、容器代理对象的Allocator),并需要将原Allocator实例转换为这些rebind后的Allocator类型。原代码中的Allocator没有提供跨类型的拷贝构造函数,编译器无法完成Allocator<_Tree_node<...>>到Allocator<_Container_proxy>的转换,导致编译报错。
  2. 修复原理:添加模板拷贝构造函数template <class U> Allocator(const Allocator<U>&) noexcept {}后,允许任意类型的Allocator实例构造当前Allocator实例,满足了MSVC STL对Allocator之间可转换性的要求,解决了类型转换失败的问题。
  3. 显式声明默认构造函数是为了避免模板构造函数覆盖默认构造行为,确保Allocator可以被无参构造。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:03:37