std::pmr::polymorphic_allocator类型错误为何未被编译期检测到
我在开发中发现,pmr::polymorphic_allocator类型不匹配的问题似乎无法在编译期被正常检测到。
我定义了一个示例类MyClass,其使用pmr::polymorphic_allocator作为构造函数入参,代码如下:
// file MyClass.hpp #include <memory_resource> class Item { // class members... }; class MyClass { public: explicit MyClass(std::pmr::polymorphic_allocator<Item> allocator) { // .... } // other members };
随后我编写如下代码测试该类的使用:
#include "MyClass.hpp" #include <memory_resource> // 符合预期,该行编译无报错 auto myClass1 = MyClass(std::pmr::polymorphic_allocator<Item>()); // 不符合预期,该行同样编译无报错 auto myClass2 = MyClass(std::pmr::polymorphic_allocator<OtherType>());
其中OtherType是和Item完全无关的类型。
为了检测类型不匹配的问题,我在MyClass的构造函数中添加了静态类型校验,代码如下:
// file MyClass.hpp #include <memory_resource> #include <type_traits> class Item { // class members... }; class MyClass { public: explicit MyClass(std::pmr::polymorphic_allocator<Item> allocator) { static_assert(std::is_same<Item, decltype(allocator)::value_type>::value, "Improper allocator!"); // .... } // other members };
但添加校验后,类型不匹配的问题仍然未被编译期检测到。我使用的开发环境为GCC 10.2.0、CLion 2021.1。
问题原因
pmr::polymorphic_allocator提供了转换构造函数,会隐式转换为符合入参要求的分配器类型。它的核心逻辑是类型擦除的底层std::pmr::memory_resource指针,和分配器的value_type无关,因此polymorphic_allocator<OtherType>会被编译器自动隐式转换为polymorphic_allocator<Item>,构造函数拿到的入参allocator的value_type已经是Item,所以你写的static_assert永远不会触发。
解决方案
如果需要禁止传入其他类型的polymorphic_allocator,可以改用模板构造函数加类型约束的写法,在隐式转换发生前拦截错误类型:
#include <memory_resource> #include <type_traits> class Item { // class members... }; class MyClass { public: template<typename T> explicit MyClass(std::pmr::polymorphic_allocator<T> allocator) { static_assert(std::is_same_v<T, Item>, "Improper allocator! Allocator value_type must be Item"); // 其余初始化逻辑 } // other members };
这种写法下,传入polymorphic_allocator<OtherType>时模板参数T会被推导为OtherType,static_assert会直接触发编译报错,达到校验类型的效果。
内容的提问来源于stack exchange,提问作者VillageTech

