为何此场景下非类型模板参数不能使用auto?
为什么这个C++场景下会报错“非类型模板参数不能使用auto”?
问题代码
#include <variant> template <auto V> using ic = std::integral_constant<decltype(V), V>; enum { shake }; int milk(ic<shake>); template <class...> struct context { template <auto V> decltype(milk(ic<V>{})) get() { return std::get<decltype(milk(ic<V>{}))>(value); } std::variant<int> value; }; int main(){ context<int> c; c.get<shake>(); }
编译错误(Clang)
prog.cc:13:42: error: a non-type template parameter cannot have type 'auto'
return std::get<decltype(milk(ic{}))>(value);
^
prog.cc:3:16: note: template parameter is declared here
template
^
1 error generated.
问题分析
先澄清一个关键点:template <auto V>本身是完全合法的——C++17及以后标准支持用auto作为非类型模板参数的占位符,错误提示的指向其实有误导性,真正的问题出在std::get的模板参数使用上。
std::get针对std::variant有两个重载模板:
- 一个接受类型参数(比如
std::get<int>(var)),用于按类型提取variant的值(要求该类型在variant中唯一存在); - 另一个接受非类型的
std::size_t参数(比如std::get<0>(var)),用于按索引提取。
你的代码中,decltype(milk(ic<V>{}))的结果是int,理论上std::get<int>(value)是合法的类型参数调用,但因为这个类型是依赖于模板参数V的依赖类型,编译器在解析模板时没能正确识别它是类型参数,反而错误地将其当作了非类型参数处理。而std::get的非类型参数要求必须是std::size_t类型,这就触发了错误提示(错误信息关联到auto模板参数是因为整个表达式依赖于V)。
解决方法
你可以通过以下两种方式修复代码:
方法1:明确提取类型别名
通过using声明把依赖类型显式提取出来,让编译器清楚这是一个类型参数:
template <class...> struct context { template <auto V> decltype(milk(ic<V>{})) get() { using ResultType = decltype(milk(ic<V>{})); return std::get<ResultType>(value); } std::variant<int> value; };
方法2:直接按索引获取
既然你的std::variant只包含int一种类型,也可以直接用索引0来获取值,避免类型解析的歧义:
template <class...> struct context { template <auto V> decltype(milk(ic<V>{})) get() { return std::get<0>(value); } std::variant<int> value; };
内容的提问来源于stack exchange,提问作者W.F.
相关产品推荐
相关产品推荐

