C++模板推导能否适配隐式构造函数触发的类型转换?
C++模板推导支持隐式类型转换的解决方案
问题根因
这是C++标准明确规定的模板推导规则限制,不属于异常问题:
- 模板参数推导阶段只会考虑3类基础隐式转换:派生类到基类的向上转型、cv限定符增减转换、数组/函数到指针的退化转换,用户定义的隐式转换(包括构造函数转换、自定义类型转换运算符)在推导阶段会被直接忽略,这就是你观察到子类场景正常、构造函数转换场景失效的原因。
- 类模板推导指南仅作用于类本身的实例化推导逻辑,无法影响函数模板的参数推导,所以你添加的推导指南无法解决该问题。
- 你提到的
std::vector<T>无法在推导阶段隐式转换为std::span<T>,也是完全相同的原理。
可行解决方案
方案1:转发重载+类型萃取(适配多参数场景,无组合爆炸问题)
只需要新增1层通用转发重载,自动完成入参到B类型的转换,不管参数数量多少都不需要额外写重载,完美解决多参数组合爆炸问题。
如果你的A类和标准容器一样定义了value_type别名,C++20版本实现如下:
#include <concepts> #include <utility> // 你原有业务逻辑的fun保持不变 template<typename... Ts> void fun(B<Ts>...); // 新增通用转发重载 template<typename... Args> requires (std::convertible_to<Args, B<typename std::remove_cvref_t<Args>::value_type>> && ...) void fun(Args&&... args) { fun(B<typename std::remove_cvref_t<Args>::value_type>(std::forward<Args>(args))...); }
如果A类没有定义value_type,可以加一个简单的类型萃取实现:
// 类型萃取:提取入参对应的B的模板参数 template<typename T> struct b_type_of; template<typename T> struct b_type_of<A<T>> { using type = T; }; template<typename T> struct b_type_of<B<T>> { using type = T; }; template<typename T> using b_type_of_t = typename b_type_of<std::remove_cvref_t<T>>::type; // 转发重载修改为 template<typename... Args> requires (std::convertible_to<Args, B<b_type_of_t<Args>>> && ...) void fun(Args&&... args) { fun(B<b_type_of_t<Args>>(std::forward<Args>(args))...); }
如果使用C++17及以下版本,把concept约束替换为enable_if即可:
template<typename... Args, typename = std::enable_if_t<(std::is_convertible_v<Args, B<b_type_of_t<Args>>> && ...)>> void fun(Args&&... args) { fun(B<b_type_of_t<Args>>(std::forward<Args>(args))...); }
方案2:非推导上下文包装(适合参数少、可显式指定模板参数的场景)
用std::type_identity把函数参数包装为非推导上下文,让模板推导不依赖入参类型,该方案需要你调用时显式指定模板参数,适合简单场景:
#include <type_traits> template<typename... Ts> void fun(std::type_identity_t<B<Ts>>...); // 调用方式 fun<int>(a);
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

