binding特化必须定义在头文件中且被调用处包含,否则跨翻译单元不可见;特化需用using type=...声明具体类型,不能用函数或operator();依赖构造参数须显式提供,无自动装配;应加static_assert校验可构造性。

binding 特化必须写在头文件里,否则跨翻译单元不可见——这是编译期 DI 最常踩的坑,没有之一。
为什么 resolve<t>()</t> 在另一个 .cpp 里调用就失败
不是函数没定义,是模板特化根本没被看到。C++ 模板特化不像普通函数,它不参与链接,只在当前翻译单元实例化时生效。
-
binding<ilogger></ilogger>写在logger.cpp里 →main.cpp包含injector.h后调用resolve<ilogger>()</ilogger>,编译器查不到特化,退回到默认实现(通常是static_assert(false)) - 即使把特化放进
logger.h,若该头没被main.cpp直接或间接包含,结果一样 - 错误提示往往指向
resolve调用点,但真正问题出在缺失特化的声明位置,而非调用逻辑
binding<t></t> 特化里不能直接返回对象,得用 using type = ...
编译期 DI 不靠运行时工厂,而是靠类型别名让 resolve<t>()</t> 知道“该造谁”。写错形式会导致 SFINAE 失效或构造失败。
- 正确:
template struct binding<ilogger> { using type = ConsoleLogger; };</ilogger> - 错误:
template struct binding<ilogger> { static ConsoleLogger instance() { return {}; } };</ilogger>—— 这不会被resolve识别 - 错误:
template struct binding<ilogger> { constexpr auto operator()() { return ConsoleLogger{}; } };</ilogger>—— 编译器无法在编译期推导返回类型用于构造
构造函数参数必须显式提供,resolve<t>()</t> 不会自动递归解析依赖
这不是 Spring 或 boost::di,没有“自动装配”概念。C++ 模板无法在不实例化的情况下反射构造函数签名,所以你得自己把依赖链串起来。
- 若
UserService构造需要std::shared_ptr<ilogger></ilogger>和Database&,就不能只写resolve<userservice>()</userservice> - 得手动组合:
UserService{resolve<ilogger>(), resolve<database>()}</database></ilogger> - 或者封装辅助模板:
make_user_service<consolelogger postgresdb>()</consolelogger>,但它内部仍硬编码了参数顺序和类型 - 如果某个依赖构造本身还要参数(比如
Database(std::string host, int port)),这些值必须在调用点显式传入,容器不接管
static_assert 报错位置容易偏移,得靠 std::is_constructible_v 提前卡住
接口契约不满足时,错误常出现在 new T{...} 那一行,而不是 resolve<t>()</t> 调用处。这会让定位变慢,尤其当 T 是深层依赖时。
- 推荐在
resolve实现里加一层校验:static_assert(std::is_constructible_v<t args...>, "T cannot be constructed with given dependencies")</t> - 避免用
requires std::is_constructible_v<t></t>做模板约束——错误堆栈会跳过你的注入层,直接报底层构造失败 - 若
ConsoleLogger忘记继承ILogger或虚函数签名不一致,std::is_constructible_v<ilogger consolelogger></ilogger>会为 false,但真正报错点可能在inject_impl<userservice>::get()</userservice>的构造语句里
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











