如何让C++函数无需感知其const参数所用的分配器类型?
问题1:是否有方法可以让func无需感知分配器类型?
有两种常用的落地方案,都可以实现非模板的func,完全消除分配器相关的代码膨胀:
- 方案1:抽象只读接口基类
定义一个无模板参数的抽象基类,把func需要用到的所有只读操作声明为纯虚函数,再让所有Foo<Alloc>特化版本继承该基类。func直接接收基类的常量引用即可,不需要感知分配器类型。
示例代码:// 无模板参数的抽象基类 class FooReadOnly { public: virtual size_t size() const = 0; virtual int at(size_t idx) const = 0; // 其他func需要的只读接口 virtual ~FooReadOnly() = default; }; // 修改Foo的定义,继承抽象基类 template<typename Alloc> class Foo : public FooReadOnly { std::vector<int, Alloc<int>> data; public: size_t size() const override { return data.size(); } int at(size_t idx) const override { return data.at(idx); } // 其他原有成员... }; // 非模板的func实现 void func(const FooReadOnly& foo); - 方案2:实现无分配器依赖的只读视图
构造一个FooView类,内部只存储func需要的只读数据指针、长度等信息,做类型擦除,不携带分配器相关的模板参数。所有Foo<Alloc>实例都可以转换为FooView,func直接接收FooView即可,该方案没有虚函数开销,性能更优。
示例代码:
调用时直接传入任意class FooView { const int* m_data; size_t m_size; public: // 泛化构造函数,支持所有Foo<Alloc>隐式转换 template<typename Alloc> FooView(const Foo<Alloc>& foo) : m_data(foo.data.data()), m_size(foo.data.size()) {} // 暴露只读接口给func调用 size_t size() const { return m_size; } int operator[](size_t idx) const { return m_data[idx]; } }; // 非模板的func实现 void func(FooView foo);Foo<Alloc>实例即可,编译器会自动完成FooView的构造,func本身只有一份实现,不会产生代码膨胀。
问题2:通过reinterpret_cast转换Foo的分配器类型是否安全?
完全不安全,属于C++标准明确规定的未定义行为。
不同分配器实例化的Foo是完全独立、无关联的类型,即使你使用的自定义分配器是无状态的,标准也不保证std::vector不同特化版本的内存布局一致。部分带状态的自定义分配器会导致std::vector内部存储额外的分配器状态,内存布局和std::allocator版本的vector差异极大,reinterpret_cast之后访问成员大概率会读到脏数据,甚至直接触发程序崩溃,绝对不要在生产代码中使用这种写法。
内容的提问来源于stack exchange,提问作者Lix
相关产品推荐
相关产品推荐

