C++结构体多参数包模板推导问题:为何与函数表现不同?
可行实现方案
C++模板规则明确要求模板参数包必须是模板参数列表的最后一个,因此直接声明两个独立的参数包是不允许的。以下是几种符合规则的实现方案:
方案1:用tuple包装其中一个参数包
将第一个参数包装入std::tuple,避免出现多个末尾参数包,再通过特化展开:
#include <iostream> #include <string> #include <tuple> template <typename T1, typename T2> struct base { }; // 辅助模板,用tuple存储第一个参数包 template <typename T1Tuple, typename... T2> struct child_impl; // 特化展开tuple中的T1参数包,完成配对继承 template <typename... T1, typename... T2> struct child_impl<std::tuple<T1...>, T2...> : base<T1, T2>... { child_impl(T2... args) { ((std::cout << "配对类型: " << typeid(T1).name() << " ↔ " << typeid(T2).name() << "\n"), ...); } }; // 外层别名简化使用方式 template <typename... T1, typename... T2> using child = child_impl<std::tuple<T1...>, T2...>; int main() { // 显式传入两个参数包,构造函数接收T2类型参数 child<std::string, int, long, short, long long, char> s(1, 2ll, 'a'); }
方案2:结合推导指南与配对类型
通过std::pair将T1和T2的元素一一绑定,配合推导指南让编译器自动推导T2包:
#include <iostream> #include <string> #include <utility> template <typename T1, typename T2> struct base { }; // 主模板,接收配对后的类型列表 template <typename... Pairs> struct child; // 特化展开每一对<T1,T2>,完成继承 template <typename... T1, typename... T2> struct child<std::pair<T1, T2>...> : base<T1, T2>... { child(T2... args) { ((std::cout << "配对类型: " << typeid(T1).name() << " ↔ " << typeid(T2).name() << "\n"), ...); } }; // 推导指南:从构造函数参数推导T2,结合显式指定的T1生成配对 template <typename... T1, typename... T2> child(T2...) -> child<std::pair<T1, T2>...>; int main() { // 显式指定T1包,构造函数自动推导T2包 child<std::string, int, long> s(1, 2ll, 'a'); }
方案3:C++20下用索引序列确保长度匹配
如果使用C++20,可以通过std::index_sequence验证两个参数包长度一致,并完成配对:
#include <iostream> #include <string> #include <tuple> #include <utility> template <typename T1, typename T2> struct base { }; // 主模板,用tuple存储两个参数包 template <typename T1Tuple, typename T2Tuple> struct child; // 特化展开两个tuple,添加长度校验 template <typename... T1, typename... T2> struct child<std::tuple<T1...>, std::tuple<T2...>> : base<T1, T2>... { // 编译期校验两个参数包长度相同 static_assert(sizeof...(T1) == sizeof...(T2), "两个参数包长度必须一致"); child(T2... args) { ((std::cout << "配对类型: " << typeid(T1).name() << " ↔ " << typeid(T2).name() << "\n"), ...); } }; // 推导指南:从构造函数参数推导T2包 template <typename... T1, typename... T2> child(T2...) -> child<std::tuple<T1...>, std::tuple<T2...>>; int main() { child<std::string, int, long> s(1, 2ll, 'a'); }
内容的提问来源于stack exchange,提问作者McAngus
相关产品推荐
相关产品推荐

