关于C++中实现兼具多态统一处理与连续实体内存布局的混合类型序列传递方案的可行性问询
C++中实现兼具多态统一处理与连续实体内存布局的混合类型序列传递方案的可行性问询
各位C++同行们好,我目前在设计一个库的某部分模块,正在纠结要不要引入某种多态值类型。先给不太熟悉的朋友补个背景:如果有人不清楚多态值类型是什么、以及这类设计的动机,可以参考Sean Parent的Runtime Polymorphism演讲、Klaus Igleberger对比std::variant与类层级结构的演讲,相关的技术讨论内容也能找到不少参考。
我的核心场景
我现在有两个还没写代码的类B和C,它们有部分共通的数据成员和方法,但并非完全一致。而且因为代码还没落地,我可以自由调整它们的内存布局、继承关系,甚至可以加填充字段或额外成员。
我的核心需求是:要把一个由B或C对象组成的连续序列,传给某个函数void f()。为了简化问题,假设这个序列用std::span来传递。
现有可选方案的痛点
我已经想了几种常规方案,但每个都有明显的缺陷:
方案1:基于继承与指针的传统多态
定义一个基类A来提取B和C的公共逻辑,让B和C公开继承A,然后把f()声明为:
void f(std::span<A*> items);
- 问题:哪怕是全B或全C的
std::vector,也必须额外构造一个指针序列才能传给f(),而且这个构造工作大概率要甩给我的库用户来做,使用成本太高。
方案2:Sean Parent风格的多态值类型
这种方式虽然是值语义的多态,但每个对象都要做一次堆分配。而且底层容器(比如vector)的内存里存的不是B或C的实体,而是指向堆对象的指针和可能的删除器数据。
- 问题:如果我手里有
std::span<C>,没办法直接(或通过转址、提取data()等方式)传给f(),必须单独构造f()需要的输入序列,太繁琐。
方案3:使用std::variant<B,C>
把序列存成std::variant<B,C>的连续集合,传给f()。
- 问题:
std::variant本身不具备多态性,所以f()里没办法统一处理每个元素,必须逐个解包、判断类型后再处理,这是我想极力避免的逻辑。
我的理想目标(鱼和熊掌要兼得)
我现在想实现一种完美的方案,同时满足以下所有条件,不知道这是不是天方夜谭:
- 传给
f()的参数是std::span<Something>,其中Something是具体类型(也就是说f()不能是模板函数,必须是固定签名的非模板函数) - 传入序列的底层内存中,直接存储的是B或C的实体数据(不是指针、删除器这类间接数据)
- 不需要为每个元素做堆分配
- 可以直接将
std::span<B>或std::span<C>当作std::span<Something>来传递(不需要额外的转换或构造步骤) - 全程不能出现未定义行为
提问
请问这种方案是完全不可能实现的空想,还是有可行的办法?如果可以实现,麻烦尽量用最早能支持的C++标准版本,并说明依赖的标准特性。
内容来源于stack exchange
相关产品推荐
相关产品推荐

