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链接失败】。
立即学习“C++免费学习笔记(深入)”;
实现类型绑定与自动实例化
方法一:基于partial specialization的显式绑定
声明template<typename Service> struct Binder;,然后对每个需注入的服务特化,例如template<> struct Binder<DatabaseService> { static auto get() { return std::make_unique<MySQLImpl>(); } };。这种方式最直观,但每加一个服务就要写一次特化。
方法二:基于ADL查找的隐式绑定(推荐)
在服务实现类所在命名空间内定义自由函数make_unique_for_inject,例如namespace myapp { auto make_unique_for_inject(DatabaseService) { return std::make_unique<MySQLImpl>(); } }。Container在实例化时通过ADL调用该函数,无需修改Binder定义。这一步的关键是【函数参数必须是Service类型而非实现类,否则ADL无法触发】。
方法三:支持工厂函数的可配置绑定
增加bind_factory模板,允许传入lambda或函数对象,如Container::bind_factory<LoggerService>([]{ return std::make_unique<FileLogger>("app.log"); });。该lambda必须是constexpr可调用的,否则无法在静态初始化阶段执行。
注入逻辑的模板递归展开实现
第一步:定义inject_all模板,接收目标类类型Target和参数包Args...
第二步:使用decltype(&Target::ctor)提取构造函数类型,再用std::is_constructible_v<Target, Args...>验证参数是否匹配
第三步:若匹配,则用std::make_from_tuple<Target>(std::forward_as_tuple(args...))完成构造;否则触发SFINAE回退到下一个重载
第四步:对Args...中的每个类型T,递归调用resolve_dependency<T>()获取其实例,resolve_dependency内部根据T是否已注册、是否有默认构造、是否支持ADL工厂等分支选择实例化路径。这里不能用dynamic_cast,也不能抛异常——所有分支必须在编译期决定。
完整可编译源码片段
struct Service {}; template<typename T> struct is_service : std::is_base_of<Service, T> {}; template<typename T> constexpr bool is_service_v = is_service<T>::value;
template<typename... Ts> struct Container { private: template<typename T> static constexpr auto resolve() { if constexpr (is_service_v<T>) { if constexpr (has_make_unique_for_inject_v<T>) { return make_unique_for_inject(std::declval<T>()); } else { return std::make_unique<T>(); } } else { static_assert(sizeof(T) == 0, "non-service type cannot be resolved by Container"); } } public: template<typename Target> static constexpr auto create() { return std::make_from_tuple<Target>(std::make_tuple(resolve<Ts>()...)); } };
template<typename T> inline constexpr bool has_make_unique_for_inject_v = []{ if constexpr (requires { make_unique_for_inject(std::declval<T>()); }) { return true; } else { return false; } }();


















