一:概述
模板在 C++ 中是编译期展开的,不同模板参数会生成不同的代码,这使得模板类/函数天然不具备 ABI 稳定性。为了保持ABI稳定,接口不要直接用模板,先用普通类打个底,模板只是“外壳”,这样 ABI 才稳定。这样做有两个好处:
-
所有模板实例共享一个实现代码(避免为每个
List<T>
生成一份几乎相同的add_front
、unlink
等函数)。 -
核心逻辑只定义一次,因此更容易保持 ABI 稳定性,并且编译速度更快。
二:示例
Link_base
是一个通用的双向链表节点结构,不带类型信息,List_base
是核心链表实现,只操作 Link_base*
,实现的是无类型的插入逻辑。
struct Link_base {Link_base* suc;Link_base* pre;
};struct List_base {Link_base* first;int sz;void add_front(Link_base* p);
};
List<T>
是一个轻量级的包装类,它提供类型安全,但实际的插入操作是调用的 List_base::add_front
。这样,List<int>
、List<string>
这些实例共享一份核心代码,只在包装层做转换和封装。
template<typename T>
struct Link : Link_base {T val;
};template<typename T>
class List : List_base {
public:void put_front(const T& e) { add_front(new Link<T>{e}); }T& front() { static_cast<Link<T>*>(first)->val; }
};
三:总结
所以为了保持 ABI 稳定,本规则建议将模板代码与核心逻辑分离,使用非模板的基础类或接口作为稳定的 ABI 边界,模板只负责类型安全和包装。这种做法可以避免因模板实例化导致的代码膨胀和 ABI 不兼容问题。