|
作者:guoling,来自微信客户端团队前言背景:C++模板是一种强大的编程工具,它允许我们编写通用的、可重用的代码;问题:模板代码的一个常见问题是代码膨胀,即编译器为每个模板实例生成大量的重复代码。现代的编译器已经能够对不同编译单元里的相同模板函数进行去重,老生常谈的external模板、将模板代码与非模板代码分离等,对瘦身意义已经不大,我们仍然需要关注如何减少每一个模板实例化的大小。除了显而易见的减少实例化类型的数量(实际业务场景下其实大部分减不了),「本文主要是提供适用于一些具体场景、可实际操作的优化策略以减少C++模板代码的大小。」策略说明主要包括:模板函数:提取通用部分模板类:抽象出通用部分到基类合理使用模板小技巧:多用组合、避免使用大型对象等等。1.将模板函数的通用部分提取出来如果模板函数中有一部分代码与模板参数无关,那么可以将这部分代码提取出来,放到一个非模板函数中。这样,这部分代码只需要生成一次,而不是在每个模板实例中都生成一次。为了方便讨论,后续的例子基于这个场景:我们提供一个集中的Service单例管理器。以下是大体的框架:// 所有 Service 需要实现的接口class BaseService {public: virtual ~BaseService() = default; virtual void onServiceInit() = 0; …… std::string contextName{};};// 所有 Service 单例的统一管理中心class ServiceCenter {public: explicit ServiceCenter(const std::string& name) : _contextName(name) {} template std::shared_ptr getService() { …… } private: std::unordered_map> _serviceMap = {}; std::string _contextName; std::recursive_mutex _mutex;};1.1最简单的情形,函数大部分逻辑都是跟模板参数无关:例如,在我们的例子中,getService()函数最简单的版本可能长这样,显然,一大部分代码是与模板参数无关的,可以提取出来:class ServiceCenter {public: template std::shared_ptr getService() { auto const key = typeid(T).name(); std::lock_guard lock(_mutex); auto const itr = _serviceMap.find(key); if (itr == _serviceMap.end()) { return nullptr; } auto service = itr->second; return std::dynamic_pointer_cast(service); }};我们抽出一个非模板的函数getService(conststd::string&key),将加锁、查询map这些逻辑都挪进去,优化后:class ServiceCenter {public: std::shared_ptr getService(const std::string &key) { std::lock_guard lock(_mutex); auto const itr = _serviceMap.find(key); if (itr == _serviceMap.end()) { return nullptr; } return itr->second; } template std::shared_ptr getService() { auto const key = typeid(T).name(); auto service = getService(key); return std::dynamic_pointer_cast(service); }};1.2稍复杂的情形,函数大部分逻辑都跟模板参数有关:例如,getService()函数不但要管查询,还要按需创建新实例、初始化、以及各种异常处理,有3行代码都用了类型T:class ServiceCenter {public: template std::shared_ptr getService() { std::lock_guard lock(_mutex); auto const key = typeid(T).name(); auto const service = getService(key); if (service == nullptr) { auto const tService = std::make_shared(); tService->contextName = _contextName; setService(key, tService); tService->onServiceInit(); return tService; } else { auto const tService = std::dynamic_pointer_cast(service); if (tService == nullptr) { aerror("ServiceCenter", "tService is null"); return nullptr; } return tService; } } void setService(const std::string &key, const std::shared_ptr
|
|