纯编译期依赖注入本质是模板参数化的类型组合与静态多态展开,非运行时对象替换;它依赖std::tuple、requires约束和constexpr构造,拒绝反射与中心化容器。

纯编译期依赖注入在 C++ 里无法真正“注入”——它本质是类型组合与静态多态的编译期展开,不是运行时对象替换。你想要的,其实是 std::tuple + std::variant + 模板递归 + requires 约束构成的“依赖图构建器”,而非传统 DI 容器。
为什么不能照搬 Spring 或 Autofac 的思路?
C++ 没有反射、没有运行时类型擦除(除非手动加 std::any 或虚函数)、也没有中心化容器注册表。所谓“编译期 DI”,实际是:
- 把依赖关系写成模板参数列表,例如
class ServiceA<dep1 dep2></dep1> - 用
constexpr函数或if constexpr控制构造路径 - 靠 SFINAE 或 C++20
requires做接口契约检查,失败直接编译报错 - 所有“注入点”必须在实例化时确定,无法延迟到
main()之后
injector<t>::resolve<service>()</service></t> 这类调用根本不能存在
如果你看到某份代码里写了类似 injector.resolve<database>()</database>,那它要么用了宏模拟(不安全),要么底层偷偷用了 std::map<type_id std::function>></type_id>(已脱离编译期)。真编译期方案只接受:
template<typename db typename logger>
struct UserService {
DB db;
Logger logger;
UserService(DB d, Logger l) : db(d), logger(l) {}
};
// 使用时:UserService<mockdb consolelogger> svc{MockDB{}, ConsoleLogger{}};
</mockdb></typename>
——所有类型和值都在模板实参和构造函数参数中显式写出,无隐式查找、无字符串名称、无运行时 map 查表。
一个可用的轻量级编译期依赖组装器(C++20)
核心目标:给定一组服务类型,自动推导依赖链并构造嵌套对象。关键限制是:每个服务必须定义 using deps = std::tuple<depa depb>;</depa> 和一个接受对应参数的构造函数。
实操要点:
- 用
std::tuple_cat展开依赖元组,再用std::make_from_tuple构造服务 - 依赖循环会触发无限模板实例化,需靠
static_assert(!std::is_same_v<t u>)</t>在关键节点拦截 - 避免使用
auto返回类型推导,否则依赖链会丢失类型信息,导致后续requires判定失效 - 示例节选(仅关键骨架):
template<typename service>
consteval auto make_service() {
if constexpr (requires { Service::deps{}; }) {
constexpr auto deps_tuple = Service::deps{};
return Service{std::make_from_tuple<service>(deps_tuple)};
} else {
return Service{};
}
}
</service></typename>
注意:consteval 强制编译期求值,但要求所有依赖类型也支持 consteval 构造 —— 很多标准库类型(如 std::string)不满足,此时只能退回到 constexpr + 运行时初始化。
最容易被忽略的硬伤:头文件爆炸与编译时间失控
每增加一个服务类型,就可能触发数十个模板实例化;依赖深度超过 4 层后,Clang/GCC 的错误信息会变成不可读的嵌套模板栈。真实项目中更可行的做法是:
- 只对核心抽象层(如
Repository<t></t>、Handler<cmd></cmd>)做编译期组合 - 具体实现(
PostgresRepo、HttpHandler)用工厂函数返回std::unique_ptr,保持运行时灵活性 - 用
#include <concepts></concepts>替代宏做约束,但别滥用requires嵌套,它会让编译器更难诊断
所谓“完整源码”,其实是一组可组合的类型契约 + 构造策略,而不是一个能直接 #include 就用的黑盒库。真要落地,得按你自己模块的生命周期和测试需求,一处处切开依赖、显式传参、放弃“自动发现”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











