为何MSVC要求C++分配器必须实现模板构造函数?
关于MSVC对C++分配器模板构造函数的强制要求
在学习C分配器时发现一个差异:MSVC编译器要求自定义分配器必须实现针对不同类型的模板构造函数,但g和clang没有这个强制要求。当省略该构造函数时,使用std::map会触发编译错误,而std::vector可以正常编译。
触发错误的最小代码示例
#include <map> #include <vector> template<class T> struct ContiguousAllocator { public: using value_type = T; ContiguousAllocator() noexcept {} // 取消注释下面的模板构造函数即可消除错误 //template<class U> //ContiguousAllocator(const ContiguousAllocator<U>& v) noexcept //{} ~ContiguousAllocator() noexcept {} T* allocate(std::size_t n) { return nullptr; } void deallocate(T* p, std::size_t n) {} }; template<class T, class U> constexpr bool operator==(const ContiguousAllocator<T>&, const ContiguousAllocator<U>&) noexcept { return false; } template<class T, class U> constexpr bool operator!=(const ContiguousAllocator<T>&, const ContiguousAllocator<U>&) noexcept { return true; } int main(int argc, char* argv[]) { std::vector<int, ContiguousAllocator<int>> vector; std::map<int, int, std::greater<int>, ContiguousAllocator<std::pair<const int, int>>> map; return 0; }
编译错误信息
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): error C2440: 'static_cast': 无法从“ContiguousAllocator<_Newfirst>”转换为“ContiguousAllocator<_Newfirst>” with [ _Newfirst=std::_Tree_node<std::pair<const int,int>,void *> ] 和 [ _Newfirst=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: “ContiguousAllocator<_Newfirst>::ContiguousAllocator”: 没有重载函数可以转换所有参数类型 with [ _Newfirst=std::_Container_proxy ] C:\User\Code\main.cpp(56): note: 可能是“ContiguousAllocator<_Newfirst>::ContiguousAllocator(const ContiguousAllocator<_Newfirst> &)” with [ _Newfirst=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: “ContiguousAllocator<_Newfirst>::ContiguousAllocator(const ContiguousAllocator<_Newfirst> &)”: 无法将参数 1 从“ContiguousAllocator<_Newfirst>”转换为“const ContiguousAllocator<_Newfirst> &” with [ _Newfirst=std::_Container_proxy ] 和 [ _Newfirst=std::_Tree_node<std::pair<const int,int>,void *> ] 和 [ _Newfirst=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: 原因: 无法从“ContiguousAllocator<_Newfirst>”转换为“const ContiguousAllocator<_Newfirst>” with [ _Newfirst=std::_Tree_node<std::pair<const int,int>,void *> ] 和 [ _Newfirst=std::_Container_proxy ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: 没有可用的用户定义转换运算符来执行此转换,或者无法调用该运算符 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: 尝试匹配参数列表“(ContiguousAllocator<_Newfirst>)”时 with [ _Newfirst=std::_Tree_node<std::pair<const int,int>,void *> ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): note: 模板实例化上下文(最早的优先)是 C:\User\Code\main.cpp(61): note: 正在编译类模板实例化“std::map<int,int,std::greater<int>,ContiguousAllocator<std::pair<const int,int>>>” C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\map(72): note: 正在编译类模板实例化“std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>” with [ _Kty=int, _Ty=int, _Pr=std::greater<int>, _Alloc=ContiguousAllocator<std::pair<const int,int>> ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1071): note: 正在编译类模板成员函数“std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree(void) noexcept” with [ _Kty=int, _Ty=int, _Pr=std::greater<int>, _Alloc=ContiguousAllocator<std::pair<const int,int>> ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\map(369): note: 在“std::map<int,int,std::greater<int>,ContiguousAllocator<std::pair<const int,int>>>::~map”中第一次引用“std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::~_Tree” with [ _Kty=int, _Ty=int, _Pr=std::greater<int>, _Alloc=ContiguousAllocator<std::pair<const int,int>> ] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1075): error C2530: “_Alproxy”: 引用必须初始化 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1076): error C3536: “_Alproxy”: 在初始化前无法使用 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1076): error C2672: “_Delete_plain_internal”: 未找到匹配的重载函数 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xmemory(1112): note: 可能是“void std::_Delete_plain_internal(_Alloc &,_Alloc::value_type *const ) noexcept” C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1076): note: 未能特化函数模板“void std::_Delete_plain_internal(_Alloc &,_Alloc::value_type *const ) noexcept” C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1076): note: 使用以下模板参数: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include\xtree(1076): note: “_Alloc=int”
编译参数
FAILED: Allocator/CMakeFiles/Allocator.dir/Allocator.cpp.obj C:\PROGRA~1\MICROS~2\2022\COMMUN~1\VC\Tools\MSVC\1440~1.338\bin\Hostx64\x64\cl.exe /nologo /TP /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Ob0 /Od /RTC1 -std:c++20 -ZI /showIncludes /FoAllocator\CMakeFiles\Allocator.dir\Allocator.cpp.obj /FdAllocator\CMakeFiles\Allocator.dir\ /FS -c C:\User\Code\Allocator\Allocator\Allocator.cpp
解决方法与原因
取消注释代码中针对U类型的模板构造函数,即可消除编译错误。
本质原因是:MSVC的STL实现(比如std::map底层的_Tree结构)在内部会使用不同类型的分配器实例——比如分配树节点的分配器和分配内部代理对象的分配器,需要通过模板构造函数来完成不同实例之间的转换。这其实符合C++标准对分配器的可转换性要求:标准规定,分配器类型必须支持从同模板的其他实例构造,而模板构造函数是实现这一点的常规方式。
g和clang的STL实现可能在内部做了兼容处理,或者对分配器转换的要求更宽松,但严格来说,实现这个模板构造函数是符合C标准的规范做法。
内容的提问来源于stack exchange,提问作者qloq
相关产品推荐
相关产品推荐

