C++变参模板参数推导失败:自定义推导指南不生效问题求解
问题原因
- 你编写的推导指南中,模板参数
D没有出现在构造函数的参数列表中,属于非推导上下文,编译器无法自动推导D的具体类型,因此该推导指南无法生效。 - C++的类模板参数推导(CTAD)规则不支持「部分显式指定模板参数、剩余参数自动推导」的用法:你写的
Foo<float>相当于已经显式指定了第一个模板参数,剩余的Cs...参数不会触发自动推导,必须全部显式声明才能通过编译。 - 额外注意:你原有代码中直接用转发引用推导出的
Cs类型会携带引用属性,直接存入tuple会产生悬空引用隐患,需要对类型做退化处理。
修复方案
方案1:使用工厂函数实现部分参数显式指定(最贴合原有写法习惯)
函数模板支持显式指定部分模板参数,剩余参数自动推导,是成本最低的修改方案:
#include <utility> #include <tuple> #include <type_traits> template <typename D, typename... Cs> struct Foo { Foo(int other_variable, Cs&&... cs) : tuple {std::make_tuple(std::forward<Cs>(cs)...)} { } std::tuple<Cs...> tuple; }; // 新增工厂函数 template <typename D, typename... Cs> auto make_foo(int other_variable, Cs&&... cs) { // std::decay_t去掉参数的引用、cv限定符,避免tuple存储引用类型 return Foo<D, std::decay_t<Cs>...>(other_variable, std::forward<Cs>(cs)...); }
调用方式修改为:
auto foo = make_foo<float>(1, []() { /* lambda type 1 */ }, [](std::string) { /* lambda type 2 */ }, [](int) { /* lambda type 3 */ } );
方案2:调整类设计,使用标签触发CTAD
如果你一定要使用类模板参数推导,可以新增无内存开销的类型标签传递显式指定的D类型:
#include <utility> #include <tuple> #include <type_traits> // 类型标签,仅用于传递类型信息 template <typename T> struct type_tag {}; template <typename D, typename... Cs> struct Foo { // 构造函数第一个参数接收类型标签 Foo(type_tag<D>, int other_variable, Cs&&... cs) : tuple {std::make_tuple(std::forward<Cs>(cs)...)} { } std::tuple<Cs...> tuple; }; // 有效推导指南,从标签里获取D的类型,Cs从后续参数推导 template <typename D, typename... Cs> Foo(type_tag<D>, int, Cs&&...) -> Foo<D, std::decay_t<Cs>...>;
调用方式修改为:
Foo foo(type_tag<float>{}, 1, []() { /* lambda type 1 */ }, [](std::string) { /* lambda type 2 */ }, [](int) { /* lambda type 3 */ } );
内容的提问来源于stack exchange,提问作者Jes
相关产品推荐
相关产品推荐

