为何指定前两参数后可变参数模板类的模板参数推导失效?
搞定可变参数模板类的推导问题
嘿,你的问题核心在于类模板的推导指南没办法凭空推导Interface和Implementation这两个参数——推导指南里的所有模板参数要么得能从构造函数实参里找到线索,要么有默认值,而你的前两个参数既没有默认值,构造函数里也完全没用到这两个类型的参数,编译器根本不知道该怎么填这俩位置,所以你的推导指南相当于白写了。
下面给你两个实用的解决方案,挑你喜欢的来:
方案一:用工厂函数(最省心)
函数模板的参数推导比类模板灵活多了,我们写个小工具函数,让你显式指定前两个固定的模板参数,剩下的构造参数交给编译器自动推导:
// 新增这个工厂函数 template <typename Interface, typename Implementation, typename... CtorArgs> auto make_wrapper(CtorArgs&&... args) { return Wrapper<Interface, Implementation, std::decay_t<CtorArgs>...>( std::forward<CtorArgs>(args)... ); }
然后在main里这么用就行:
// 这下不用手动写<int>了,编译器会自动推导 auto wrapperB = make_wrapper<InterfaceB, ImplementationB>(7); wrapperB.GetService().Serve();
这里加std::decay_t是为了避免把引用类型存到tuple里(毕竟构造函数用的是转发引用,推导后可能是左值引用),防止后续出现悬空引用的坑,稳一点。
方案二:调整类模板结构(可选)
如果你不想加额外的函数,也可以改改类模板的写法,给可变参数加个默认值,再用模板构造函数来推导参数:
// 给CtorArgs加个空的默认参数 template <typename Interface, typename Implementation, typename... CtorArgs = void> class Wrapper { public: // 用模板构造函数来推导构造参数类型 template <typename... Args> Wrapper(Args&&... args) : m_ctorArgs(std::forward<Args>(args)...) {} Service& GetService() { // 这里用auto来接收构造参数,不用硬绑CtorArgs了 m_service = std::apply([](auto... ctorArgs) { return std::make_unique<Implementation>(std::forward<decltype(ctorArgs)>(ctorArgs)...); }, m_ctorArgs); return *m_service; } private: // 让tuple的类型自动推导(C++17及以上支持) auto m_ctorArgs = std::make_tuple(std::forward<Args>(args)...); std::unique_ptr<Service> m_service; };
不过这种写法会让类的内部逻辑复杂一点,不如工厂函数直观,所以更推荐第一种方案。
顺便提个小优化
你的GetService每次调用都会重新new一个实例,原来的m_service会被直接覆盖销毁。如果这不是你想要的“按需创建”,可以加个判断,只在第一次调用时初始化:
Service& GetService() { if (!m_service) { // 只有当实例不存在时才创建 m_service = std::apply([](auto... ctorArgs) { return std::make_unique<Implementation>(std::forward<decltype(ctorArgs)>(ctorArgs)...); }, m_ctorArgs); } return *m_service; }
这样更符合“按需创建”的初衷,不会重复造实例~
内容的提问来源于stack exchange,提问作者banzai
相关产品推荐
相关产品推荐

