pmr容器emplace_back含allocator_type类触发uses_allocator静态断言失败
我定义了PmrCustomer类,该类已实现uses-allocator构造协议——包含allocator_type类型定义,且提供了带尾部分配器参数的构造函数,但未显式定义拷贝构造函数或移动构造函数。随后创建pmr::vector容器,尝试调用emplace_back传入pmr::string对象,原本以为会调用我提供的唯一构造函数,但编译时出现错误。显式添加带尾部分配器的拷贝构造和移动构造函数后问题解决,想了解未显式提供这些构造函数时错误产生的原因。
代码示例
#include <string> #include <memory_resource> #include <iostream> class PmrCustomer { private: std::pmr::string name; public: using allocator_type = std::pmr::polymorphic_allocator<char>; PmrCustomer(std::pmr::string n, allocator_type alloc = {}) : name{std::move(n), alloc}, allocator_(alloc) { } //PmrCustomer(const PmrCustomer& c, allocator_type alloc) : name{c.name, alloc} { //} //PmrCustomer(PmrCustomer&& c, allocator_type alloc) : name{std::move(c.name), alloc} { //} // setter/getter: void setName(std::pmr::string s) { name = std::move(s); } std::pmr::string getName() const { return name; } std::string getNameAsString() const { return std::string{name}; } allocator_type allocator_; }; int main() { std::pmr::vector<PmrCustomer> coll(std::pmr::get_default_resource()); coll.reserve(100); std::pmr::string c2 {"peter"}; coll.emplace_back(c2); for (const auto& cust : coll) { std::cout << cust.getName() << '\n'; } }
编译环境
- Ubuntu20.04
- gcc-9
- 编译选项:
-std=c++17
报错详情
In file included from /usr/include/c++/9/memory:77, from /usr/include/c++/9/memory_resource:37, from d.cpp:2: /usr/include/c++/9/bits/uses_allocator.h: In instantiation of ‘struct std::__uses_alloc<true, PmrCustomer, std::pmr::polymorphic_allocator<PmrCustomer>, PmrCustomer>’: /usr/include/c++/9/type_traits:1294:12: required from ‘struct std::is_base_of<std::__uses_alloc0, std::__uses_alloc<true, PmrCustomer, std::pmr::polymorphic_allocator<PmrCustomer>, PmrCustomer> >’ /usr/include/c++/9/type_traits:2991:25: required from ‘constexpr const bool std::is_base_of_v<std::__uses_alloc0, std::__uses_alloc<true, PmrCustomer, std::pmr::polymorphic_allocator<PmrCustomer>, PmrCustomer> >’ /usr/include/c++/9/memory_resource:243:18: required from ‘typename std::pmr::polymorphic_allocator<_Tp>::__not_pair<_Up>::type std::pmr::polymorphic_allocator<_Tp>::construct(_Tp1*, _Args&& ...) [with _Tp1 = PmrCustomer; _Args = {PmrCustomer}; _Tp = PmrCustomer; typename std::pmr::polymorphic_allocator<_Tp>::__not_pair<_Up>::type = void]’ /usr/include/c++/9/bits/alloc_traits.h:244:4: required from ‘static std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = PmrCustomer; _Args = {PmrCustomer}; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]’ /usr/include/c++/9/bits/alloc_traits.h:350:16: required from ‘static decltype (std::allocator_traits<_Alloc>::_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = PmrCustomer; _Args = {PmrCustomer}; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; decltype (std::allocator_traits<_Alloc>::_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = void]’ /usr/include/c++/9/bits/stl_uninitialized.h:293:25: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::move_iterator<PmrCustomer*>; _ForwardIterator = PmrCustomer*; _Allocator = std::pmr::polymorphic_allocator<PmrCustomer>]’ /usr/include/c++/9/bits/stl_vector.h:1511:35: required from ‘std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::move_iterator<PmrCustomer*>; _Tp = PmrCustomer; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::vector<_Tp, _Alloc>::pointer = PmrCustomer*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’ /usr/include/c++/9/bits/vector.tcc:85:14: required from ‘void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = PmrCustomer; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’ d.cpp:77:21: required from here /usr/include/c++/9/bits/uses_allocator.h:98:53: error: static assertion failed: construction with an allocator must be possible if uses_allocator is true 98 | is_constructible<_Tp, _Args..., const _Alloc&>>::value, | ^~~~~ In file included from d.cpp:2: /usr/include/c++/9/memory_resource: In instantiation of ‘typename std::pmr::polymorphic_allocator<_Tp>::__not_pair<_Up>::type std::pmr::polymorphic_allocator<_Tp>::construct(_Tp1*, _Args&& ...) [with _Tp1 = PmrCustomer; _Args = {PmrCustomer}; _Tp = PmrCustomer; typename std::pmr::polymorphic_allocator<_Tp>::__not_pair<_Up>::type = void]’: /usr/include/c++/9/bits/alloc_traits.h:244:4: required from ‘static std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = PmrCustomer; _Args = {PmrCustomer}; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]’ /usr/include/c++/9/bits/alloc_traits.h:350:16: required from ‘static decltype (std::allocator_traits<_Alloc>::_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = PmrCustomer; _Args = {PmrCustomer}; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; decltype (std::allocator_traits<_Alloc>::_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = void]’ /usr/include/c++/9/bits/stl_uninitialized.h:293:25: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::move_iterator<PmrCustomer*>; _ForwardIterator = PmrCustomer*; _Allocator = std::pmr::polymorphic_allocator<PmrCustomer>]’ /usr/include/c++/9/bits/stl_vector.h:1511:35: required from ‘std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::move_iterator<PmrCustomer*>; _Tp = PmrCustomer; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::vector<_Tp, _Alloc>::pointer = PmrCustomer*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’ /usr/include/c++/9/bits/vector.tcc:85:14: required from ‘void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = PmrCustomer; _Alloc = std::pmr::polymorphic_allocator<PmrCustomer>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’ d.cpp:77:21: required from here /usr/include/c++/9/memory_resource:249:6: error: no matching function for call to ‘PmrCustomer::PmrCustomer(PmrCustomer, std::pmr::polymorphic_allocator<PmrCustomer>&)’ 249 | ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ d.cpp:50:5: note: candidate: ‘PmrCustomer::PmrCustomer(std::pmr::string, PmrCustomer::allocator_type)’ 50 | PmrCustomer(std::pmr::string n, allocator_type alloc = {}) : name{std::move(n), alloc}, allocator_(alloc) { | ^~~~~~~~~~~ d.cpp:50:34: note: no known conversion for argument 1 from ‘PmrCustomer’ to ‘std::pmr::string’ {aka ‘std::__cxx11::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> >’} 50 | PmrCustomer(std::pmr::string n, allocator_type alloc = {}) : name{std::move(n), alloc}, allocator_(alloc) { | ~~~~~~~~~~~~~~~~~^ d.cpp:43:7: note: candidate: ‘PmrCustomer::PmrCustomer(const PmrCustomer&)’ 43 | class PmrCustomer | ^~~~~~~~~~~ d.cpp:43:7: note: candidate expects 1 argument, 2 provided d.cpp:43:7: note: candidate: ‘PmrCustomer::PmrCustomer(PmrCustomer&&)’ d.cpp:43:7: note: candidate expects 1 argument, 2 provided
错误原因解析
错误触发点在
reserve的内存重分配逻辑
你调用coll.reserve(100)时,vector会尝试分配更大的内存块。虽然此时容器为空,但编译器在实例化vector的内存重分配模板时,会检查如何构造新内存中的元素——包括从旧元素移动/拷贝的场景。PMR分配器会严格遵循uses-allocator构造协议,尝试在构造对象时自动追加分配器参数。默认生成的拷贝/移动构造不兼容uses-allocator协议
因为你的类定义了allocator_type,标准库会判定它支持uses-allocator构造。但编译器自动生成的默认拷贝构造是PmrCustomer(const PmrCustomer&),默认移动构造是PmrCustomer(PmrCustomer&&),都只接受1个参数。当标准库尝试以PmrCustomer(源对象, 分配器)的形式构造新对象时,找不到匹配的构造函数,直接触发编译错误。静态断言的触发逻辑
标准库的uses_allocator相关模板会做静态检查:如果一个类标记为支持分配器(即存在allocator_type),那么必须能以「原有构造参数 + 分配器」的形式构造对象。当检查到无法用PmrCustomer对象加分配器构造新实例时,就会触发uses_allocator.h中的静态断言,提示构造失败。
内容的提问来源于stack exchange,提问作者Jerry2102

