C++17推导指南返回类型非特化:进阶类模板实参推导求助
解决C++17类模板推导中重复类型的问题
这个问题我之前也碰到过!C++17的类模板推导指南确实有个限制——->后面只能直接写模板特化,没法直接展开参数包或者用复杂的类型计算结果。不过借助辅助元函数(类型 traits),完全能搞定你说的场景。
先明确你的核心需求:
- 一个可变参数模板类
MyClass<Ts...>,支持两种构造方式:- 直接传入任意数量的不同类型参数,推导对应
Ts...; - 传入单个类型值+编译时重复次数,自动推导成包含N次该类型的
MyClass<T, T, ..., T>。
- 直接传入任意数量的不同类型参数,推导对应
下面是完整的实现方案:
1. 定义基础可变参数类
先写出类的框架和两个构造函数,注意第二个构造函数用std::integral_constant传递编译时的重复次数N(避免运行时数值的不确定性):
#include <utility> #include <type_traits> template<typename... Ts> struct MyClass { // 构造函数1:接受任意数量不同类型的参数 MyClass(Ts&&...) {} // 构造函数2:接受单个类型,以及编译时的重复次数标记 template<typename T, size_t N> MyClass(const T&, std::integral_constant<size_t, N>) {} };
2. 实现辅助类型生成工具
我们需要一个工具,能根据T和N生成MyClass<T, T, ..., T>。用std::index_sequence来实现最简洁高效:
// 辅助函数模板:利用index_sequence展开N个T类型 template<typename T, size_t... Is> constexpr auto make_repeat_types(std::index_sequence<Is...>) -> MyClass<decltype((void)Is, std::declval<T>())...>; // 类型别名:封装推导结果,对外提供清晰的接口 template<typename T, size_t N> using RepeatMyClass = decltype(make_repeat_types<T>(std::make_index_sequence<N>()));
这里的(void)Is是为了避免编译器警告未使用的模板参数,逗号表达式的最终结果是std::declval<T>()的类型,也就是T——这样每个Is都会对应一个T,最终展开成N个T的参数包。
3. 编写自定义推导指南
现在就可以用RepeatMyClass这个类型别名来写推导指南,完美绕过->后面不能直接展开参数包的限制:
// 为第二个构造函数绑定推导规则 template<typename T, size_t N> MyClass(const T&, std::integral_constant<size_t, N>) -> RepeatMyClass<T, N>;
4. 测试验证
现在你就能按预期使用这个类了:
// 简化编译时常量的写法(可选) template<size_t N> using size_c = std::integral_constant<size_t, N>; int main() { // 构造函数1:推导为 MyClass<int, double, const char*> MyClass a(1, 3.14, "hello"); // 构造函数2:推导为 MyClass<int, int, int>(3个int) MyClass b(42, size_c<3>{}); }
补充说明
- 如果你习惯用结构体特化的方式实现辅助工具,也可以替换成递归结构体的写法,效果是一样的;
- C20之后有更简洁的写法(比如用
std::repeat_t之类的工具),但上面的方案完全兼容C17,属于标准且通用的做法。
内容的提问来源于stack exchange,提问作者nyronium
相关产品推荐
相关产品推荐

