面向结构体参数的C++类模板推导指南编写方法
类模板实参推导(CTAD)失效场景解决方案
当模板结构体作为其他模板结构体的构造入参时,存在三类常见的自动模板实参推导失效场景,对应解决方法如下:
场景1:大括号包裹的临时入参无法推导非类型模板参数
问题复现代码
#include <cstddef> #include <array> template <size_t N> struct A_args { std::array<int, N> a; }; template <size_t N> struct A { std::array<int,N> a; A(A_args<N> const& aa): a{aa.a} {}; }; int main() { A_args a{std::array{1,2,3,4}}; auto b = A(a); // 推导正常 auto c = A(A_args{std::array{1,2,3,4}}); // 推导正常 auto d = A({std::array<int,4>{1,2,3,4}}); // 推导失败 }
失效原因
大括号初始化列表(braced-init-list)属于非推导上下文,CTAD不会主动将括号内的内容匹配为A_args<N>类型,无法跨括号提取N参数。
解决方法
添加自定义类模板推导指南,直接从入参的std::array类型提取数组大小:
template<size_t N> A(std::array<int, N>) -> A<N>;
添加后d可正常推导为A<4>类型。
场景2:聚合初始化、指定初始化器场景下类型参数推导失败
问题复现代码
template <typename T> struct A_args { T a {}; }; template <typename T> struct A { A(A_args<T> const& aa = {}) : a{aa.a} {}; T a; }; int main() { auto a = A_args{4}; // 推导失败 auto b = A{{.a = 2}}; // 推导失败 }
失效原因
- C17标准下CTAD不会自动为聚合类型生成从构造参数推导模板参数的指引,
A_args{4}无法直接从入参4推导T的类型(C20对聚合CTAD做了扩展,但部分编译器仍需显式指引)。 - 外层
A的推导失败原因同场景1,大括号包裹的指定初始化器不会被自动识别为A_args<T>实例。
解决方法
分别为两个模板添加对应推导指南:
// 从A_args的初始化成员值类型推导T template<typename T> A_args(T) -> A_args<T>; // 直接从A的入参类型推导T,跳过A_args的中间匹配 template<typename T> A(T) -> A<T>;
添加后a推导为A_args<int>,b推导为A<int>。
场景3:偏特化继承场景下隐式模板参数无法推导
问题复现代码
#include <cstddef> #include <array> template <class... T> constexpr bool always_false = false; // 推导正常:构造参数直接匹配非类型参数N template<size_t N> struct A { std::array<int, N> a; A(std::array<int, N> const& a): a{a} {}; }; // 推导正常:继承后构造函数直接暴露N参数 template <size_t N> struct A1 : A<N> { A1(std::array<int, N> const& a): A<N>{a} {}; }; // 推导正常:显式指定首个类型参数为int后,可从构造参数推导N template <typename T, size_t N> struct A2 : A<N> { static_assert(always_false<T>, "virtual base"); }; template <size_t N> struct A2<int, N> : A<N> { A2(std::array<int, N> const& a): A<N>{a} {}; }; // 推导失败:主模板仅含单个类型参数T,int偏特化的N无法自动推导 template <typename T> struct A3 : A<0> { static_assert(always_false<T>, "virtual base"); }; template <size_t N> struct A3<int> : A<N> { A3(std::array<int, N> const& a): A<N>{a} {}; }; int main() { std::array a {1,2,3,4}; auto b = A(a); // 正常 auto c = A1(a); // 正常 auto d = A2<int, 4>(a); // 正常 auto e1 = A3<int>(a); // 推导失败 auto e2 = A3(a); // 推导失败 }
失效原因
- 默认CTAD仅根据类模板的主模板生成推导指引,
A3主模板仅接收一个类型参数T,继承的基类固定为A<0>,构造函数没有暴露非类型参数N的推导入口。 - 当指定
T=int或传入std::array参数时,编译器不会自动匹配A3<int>偏特化中定义的额外非类型参数N。
解决方法
为A3添加自定义推导指南,明确入参到模板参数的映射规则:
// 传入std::array<int,N>时,自动推导T为int,匹配对应偏特化 template<size_t N> A3(std::array<int, N>) -> A3<int>;
添加后:
A3(a)会自动推导为A3<int>类型,偏特化中的N从入参a的大小自动匹配为4A3<int>(a)可正常匹配偏特化的构造函数,N推导为4- 主模板的
static_assert会拦截所有非int类型的非法实例化,不会出现意外的A<0>实例。
内容的提问来源于stack exchange,提问作者user19087
相关产品推荐
相关产品推荐

