C++编译期依赖注入只能通过模板元编程实现,无法用运行时框架;std::shared_ptr/unique_ptr因类型擦除破坏编译期推导性而不适用;真正可用的是具名类型、模板参数、constexpr值和别名模板。

编译期依赖注入在 C++ 里不能靠“框架”实现,只能靠模板元编程 + 类型推导 + 构造函数约束来模拟行为;它不解决运行时解耦,而是把依赖关系固化在类型系统里——用错场景反而增加编译负担和错误晦涩度。
为什么 std::shared_ptr 和 std::unique_ptr 不适合编译期注入
它们是运行时多态载体,类型擦除(如 std::any 或虚函数)会破坏编译期可推导性。真正能参与编译期依赖解析的只有具名类型、模板参数、constexpr 值和别名模板。
- 传
std::shared_ptr<ilogger></ilogger>进模板函数?编译器看不到ILogger的具体实现,无法做 SFINAE 或requires约束 - 想让容器自动构造依赖链?必须显式写出每个类型的模板实参,比如
Container<servicea serviceb loggerimpl></servicea>,否则无法推导构造顺序 - 依赖循环(A 依赖 B,B 依赖 A)在模板实例化阶段直接报错,但错误信息往往卡在
template instantiation depth exceeded,不是逻辑错误提示
用 auto + requires 实现最小可行注入契约
核心不是“注入”,而是“验证能否构造”。所有依赖必须通过构造函数签名暴露,且支持聚合初始化或默认构造。
template<typename T>
concept Injectable = requires {
typename T::inject_dependencies;
requires std::is_constructible_v<T, typename T::inject_dependencies::type...>;
};
-
T::inject_dependencies::type是一个std::tuple类型,列出 T 所需的所有依赖类型(如std::tuple<database cache></database>) - 这个 tuple 必须在定义
T时静态写死,不能靠反射或宏生成——C++20 没有可靠字段反射 - 实际构造时用
std::make_from_tuple<t>(deps_tuple)</t>,但前提是所有依赖已作为变量存在且可绑定引用
手动组装依赖图比自动扫描更可控
没有“容器启动”概念,所谓“DI 容器”只是一个带状态的模板结构体,生命周期由作用域决定。
立即学习“C++免费学习笔记(深入)”;
template<typename... Services>
struct Injector {
std::tuple<Services...> services;
template<typename T>
constexpr auto get() & {
if constexpr (std::is_same_v<T, std::remove_cvref_t<decltype(std::get<T>(services))>>) {
return std::get<T>(services);
} else {
static_assert(sizeof(T) == 0, "T not registered in Injector");
}
}
};
- 注册即构造:
Injector<loggerimpl databaseimpl> inj{LoggerImpl{}, DatabaseImpl{}};</loggerimpl> - 获取即引用:
auto& logger = inj.get<loggerimpl>();</loggerimpl>—— 不拷贝、不 new、无运行时查找 - 不能动态增删服务:
Injector类型由模板参数完全决定,改一个依赖就得改整个类型名
最易被忽略的限制:跨编译单元依赖无法推导
如果 ServiceA 定义在 a.cpp,而 ServiceB 在 b.cpp 里需要它,那么 Injector<servicea serviceb></servicea> 必须在同一个 TU(translation unit)里实例化,否则链接器看不到 ServiceA 的完整定义,模板无法完成实例化。
这意味着你没法把“DI 配置”单独抽成一个头文件然后到处 #include——它必须和具体服务的定义或使用点强耦合。一旦服务增多,Injector<...></...> 的模板参数列表会迅速失控,此时不如回归手动传参或用 struct Context { ServiceA& a; ServiceB& b; }; 显式传递。


















