Tri*_*dle 8 c++ stl type-erasure allocator c++11
"经典"STL容器例如std::vector并将std::map其分配器类型作为模板参数.这意味着,std::vector<T, std::allocator<T>>和std::vector<T, MyAllocator>例如被视为完全独立的类型.
一些较新的分配感知类,如std::shared_ptr和std::tuple,另一方面利用类型擦除约分配器"隐藏"的信息,所以它不会形成类型签名的一部分.但是,std::unordered_map(具有类似的年份shared_ptr)保持了采用额外默认模板参数的经典方法.
问题:
处理std::vector<T, std::allocator<T>>和std::vector<T, MyAllocator>作为不同类型被认为是可取的,还是只是在STL编写时类型擦除的副作用不是众所周知的技术?
以这种方式使用类型擦除有哪些缺点(如果有的话)?
类型擦除的分配器是否总是首选新容器?
一些较新的分配感知类,如
std::shared_ptr和std::tuple,另一方面利用类型擦除约分配器"隐藏"的信息,所以它不会形成类型签名的一部分.
std::tuple根本不使用类型擦除.元组可以用分配器构造,但它只是(有条件地)将它传递给它的元素,它不会将它存储在任何地方,因为元组从不分配任何内存,因此不需要分配器.
std::shared_ptr确实会分配内存,所以它可以使用一个分配器,它将存储直到需要释放控制块.由于控制块已经对用户不可见并存储在堆上,因此与该控制块关联的分配器对用户也是不可见的.
因此,比较shared_ptr并不是非常相关,因为它对于不适用于容器的分配器具有完全不同的用途.
- 处理
std::vector<T, std::allocator<T>>和std::vector<T, MyAllocator>作为不同类型被认为是可取的,还是只是在STL编写时类型擦除的副作用不是众所周知的技术?
STL中分配器的最初动机是封装有关内存模型的详细信息,特别是分段内存的 "近"和"远"指针.这就是分配器定义pointer容器内部使用的成员的原因.例如,使用近指针的向量不得将其元素的地址与使用远指针的另一个容器中的地址混淆.
因此,对于原始用途,具有不同类型是有价值的,但这些日子原始用途是无关紧要的.
- 以这种方式使用类型擦除有哪些缺点(如果有的话)?
所有函数调用都必须是虚拟的(或者通过函数指针进行其他形式的间接调用)并且更难以内联.这不是一个问题shared_ptr,只需在擦除分配器类型之前分配一次内存然后再次使用它来释放内存,但通用容器可能会进行数千次分配.
从容器中检索类型擦除的分配器要困难得多,这使得创建容器副本变得复杂.(它应该使用分配器的副本吗?如何复制你看不到的东西?)这对于类型来说不是问题,shared_ptr因为复制a shared_ptr只会增加引用计数,它不会分配任何东西.
通常需要更大的对象sizeof(void*)来存储类型擦除的分配器.即使分配器是空的无状态类型,也不能优化掉额外的指针std::allocator<T>.与能够利用Empty Base-class Optimization来存储空分配器相比,可能意味着大小增加50%甚至100%的类型.这不是问题,shared_ptr因为除了创建或销毁控制块之外不需要分配器,因此不需要可以访问它shared_ptr以用于其他(de)分配.
因为类型擦除的分配器必须满足特定的抽象接口,所以它必须在其成员allocate和deallocate成员中使用原始指针.这意味着您不能使用自定义pointer类型,例如存储相对于基址的相对偏移量的指针,这对于Boost.Interprocess中使用的共享内存分配器非常有用.
- 类型擦除的分配器是否总是首选新容器?
我会说不.如果分配器是类型的一部分,您可以针对常见情况对其进行优化,同时仍然允许容器的用户选择在内部使用类型擦除的多态分配器,例如库基础知识TS中的类型擦除.