关于《C++程序设计语言》中类型擦除技术里“single run-time representation”含义的技术问询
咱们先从Stroustrup举的Vector<T*>特化例子说起,把这个点掰明白,再延伸到std::any这类通用类型擦除场景。
在那个Vector<T*>的部分特化实现里,所有指向不同类型的指针(比如int*、std::string*、自定义类的指针),在运行时的内存表示是完全一致的——都是固定长度的内存地址(32位系统4字节,64位8字节)。所以Vector<T*>直接继承复用了Vector<void*>的底层实现:不管T是什么,Vector<T*>的底层存储结构在运行时都是一模一样的void*数组。这就是Stroustrup这里说的“single run-time representation”:所有Vector<T*>实例的运行时存储是同一个结构,共享同一份机器码实现。
接下来解决你对std::any的疑惑:你觉得std::any能存任意大小的对象,怎么会有“单一运行时表示”?其实这里的“single run-time representation”指的是类型擦除后的统一控制结构,而非存储的实际对象本身。
std::any的底层设计是这样的:它有一个固定大小的“小缓冲区”(通常是16或32字节,具体看编译器实现),如果要存储的对象尺寸小于等于缓冲区,就直接存在这个缓冲区里;如果对象更大,就会在堆上分配内存。但不管存的是int、std::string还是自定义的大对象,std::any自身的管理结构是统一的:它会保存一个指向类型信息的指针(比如std::type_info的实例),还有一组用于销毁、拷贝对象的函数指针。这些管理结构的运行时内存布局是固定的,所有std::any实例都遵循这个布局——这就是它的“单一运行时表示”。至于实际存储的对象,差异被隐藏在这个统一的管理结构后面了,上层代码只需要通过std::any的统一接口来操作。
总结一下:类型擦除里的“single run-time representation”,核心是为不同原始类型的实例提供一个统一的运行时结构(要么是存储本身,要么是管理存储的控制块),让上层可以用完全相同的方式操作这些实例,无需关心它们的原始类型。Stroustrup的Vector<T*>是前者(因为指针大小统一,存储本身就可以复用),std::any是后者(控制结构统一,存储按需调整)。
内容来源于stack exchange

