c++编译期依赖注入容器通过模板特化、adl查找和constexpr函数实现零开销类型绑定:服务接口仅作标记,inject宏生成类型推导桩,container用tuple静态存储实例,支持显式特化、adl隐式绑定及constexpr工厂函数,所有解析在编译期完成。

用C++模板在编译期构造依赖注入容器
你需要让不同模块的类实例在不写new、不依赖运行时反射的前提下,自动按类型绑定并注入到使用者中,且所有绑定关系在编译阶段就确定、无虚函数开销、无RTTI、无动态内存分配。这要求利用模板特化、参数包展开、类型萃取和SFINAE控制实例化路径。
第一步:定义服务接口基类,仅作类型标记,不带虚析构也不含任何成员——因为编译期DI不走多态,只靠类型名匹配。
第二步:声明注入点宏INJECT(T),它生成一个静态constexpr函数,返回std::in_place_type_t,用于后续模板推导时触发特化分支。
第三步:编写核心容器模板Container,接受一个类型列表Ts...作为模板参数,内部用std::tuple<:unique_ptr>...>存储单例指针,但注意【所有指针必须为static inline,否则跨TU链接失败】。
实现类型绑定与自动实例化
方法一:基于partial specialization的显式绑定
声明template struct Binder;,然后对每个需注入的服务特化,例如template struct Binder { static auto get() { return std::make_unique(); } };。这种方式最直观,但每加一个服务就要写一次特化。
方法二:基于ADL查找的隐式绑定(推荐)
在服务实现类所在命名空间内定义自由函数make_unique_for_inject,例如namespace myapp { auto make_unique_for_inject(DatabaseService) { return std::make_unique(); } }。Container在实例化时通过ADL调用该函数,无需修改Binder定义。这一步的关键是【函数参数必须是Service类型而非实现类,否则ADL无法触发】。
方法三:支持工厂函数的可配置绑定
C++14
C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。
下载
增加bind_factory模板,允许传入lambda或函数对象,如Container::bind_factory([]{ return std::make_unique("app.log"); });。该lambda必须是constexpr可调用的,否则无法在静态初始化阶段执行。
注入逻辑的模板递归展开实现
第一步:定义inject_all模板,接收目标类类型Target和参数包Args...
第二步:使用decltype(&Target::ctor)提取构造函数类型,再用std::is_constructible_v验证参数是否匹配
第三步:若匹配,则用std::make_from_tuple(std::forward_as_tuple(args...))完成构造;否则触发SFINAE回退到下一个重载
第四步:对Args...中的每个类型T,递归调用resolve_dependency()获取其实例,resolve_dependency内部根据T是否已注册、是否有默认构造、是否支持ADL工厂等分支选择实例化路径。这里不能用dynamic_cast,也不能抛异常——所有分支必须在编译期决定。
完整可编译源码片段
struct Service {}; template struct is_service : std::is_base_of {}; template constexpr bool is_service_v = is_service::value;
template struct Container { private: template static constexpr auto resolve() { if constexpr (is_service_v) { if constexpr (has_make_unique_for_inject_v) { return make_unique_for_inject(std::declval()); } else { return std::make_unique(); } } else { static_assert(sizeof(T) == 0, "non-service type cannot be resolved by Container"); } } public: template static constexpr auto create() { return std::make_from_tuple(std::make_tuple(resolve()...)); } };
template inline constexpr bool has_make_unique_for_inject_v = []{ if constexpr (requires { make_unique_for_inject(std::declval()); }) { return true; } else { return false; } }();
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!