为何无法通过此方式获取模板的第一个实参?
咱们先拆解下你遇到的问题哈。首先先看你写的代码:
template<class> struct template_arg; template<class T, template<T> class Template, T Value> struct template_arg<Template<Value>> { static T value() { return Value; } }; template<int> struct const_int { }; int main() { return template_arg<const_int<0>>::value(); }
编译时弹出的错误是:
error: template parameters not deducible in partial specialization:
struct template_arg<Template>
^~~~~~~~~~~~~~~~~~~~~~~~~~...
问题的核心原因:模板参数推导的循环依赖
你这段代码的问题出在模板参数推导的规则限制上。在你写的部分特化版本里,Template是一个「依赖于T的模板模板参数」——也就是template<T> class Template这里,Template的模板参数类型是由外层的T决定的。
但编译器在匹配template_arg<const_int<0>>的时候,需要从const_int<0>反向推导三个参数:T、Template、Value。这里的矛盾点在于:
- 要确定
Template是什么,得先知道T的类型(因为Template是接受T类型参数的模板); - 但
T的类型又需要从Template<Value>里推导出来。
这就形成了一个循环依赖,编译器根本没办法完成这个推导过程,所以直接报错说参数无法推导。
怎么解决这个问题?
我们可以换个思路,打破这个循环依赖。根据C++版本的不同,有两种常见的写法:
1. C++17及以上:用auto模板参数
C++17引入了非类型模板参数的auto推导,我们可以直接写一个匹配任意单参数非类型模板的特化:
template<class> struct template_arg; // 匹配任意单参数非类型模板 template<template<auto> class Template, auto Value> struct template_arg<Template<Value>> { static decltype(Value) value() { return Value; } }; template<int> struct const_int { }; int main() { return template_arg<const_int<0>>::value(); // 正常编译返回0 }
2. C11/C14:针对具体类型写特化
如果用的是更早的标准,没办法用auto,那可以针对你需要的非类型参数类型(比如int)单独写特化:
template<class> struct template_arg; // 专门匹配接受int类型非类型参数的模板 template<template<int> class Template, int Value> struct template_arg<Template<Value>> { static int value() { return Value; } }; template<int> struct const_int { }; int main() { return template_arg<const_int<0>>::value(); // 同样可以正常编译 }
总结一下
你原来的写法之所以失败,本质是让编译器去解决一个「先有鸡还是先有蛋」的推导问题:它既需要T来确定Template的类型,又需要Template<Value>来推导T,这种互相依赖的推导是C++模板规则不允许的。换一种方式拆分参数,就能绕开这个限制啦。
内容的提问来源于stack exchange,提问作者user541686

