模板特化相关的using声明是否总会触发隐式实例化?
1. 已知的非法写法
以下代码属于非法写法,因为显式特化出现在模板特化的隐式实例化之后:
template<typename T> struct will_get_explicitly_specialized { using type = void; }; template<typename T> struct ordinary_template { using type = typename will_get_explicitly_specialized<T>::type; }; ordinary_template<int> x; // 此处触发ordinary_template<int>的隐式实例化 template<> struct will_get_explicitly_specialized<int> { using type = int; };
编译时通常会报类似错误:
<source>:16:8: error: specialization of 'will_get_explicitly_specialized<int>' after instantiation 16 | struct will_get_explicitly_specialized<int> { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <source>:16:8: error: redefinition of 'struct will_get_explicitly_specialized<int>' <source>:4:8: note: previous definition of 'struct will_get_explicitly_specialized<int>' 4 | struct will_get_explicitly_specialized { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Compiler returned: 1
这符合预期逻辑:ordinary_template<int> x; 定义对象时需要ordinary_template<int>的完整类型,会触发其隐式实例化,进而通过using type = ...语句触发will_get_explicitly_specialized<int>的隐式实例化。而根据标准,显式特化必须在模板特化被隐式实例化之前声明,否则行为非法。
2. 未触发诊断的写法及疑问
但以下代码在相同编译环境下不会触发诊断,需要明确其合法性:
template<typename T> struct will_get_explicitly_specialized { using type = void; }; template<typename T> struct ordinary_template { using type = typename will_get_explicitly_specialized<T>::type; }; using ot_int = ordinary_template<int>; // 顶级using别名声明 template<> struct will_get_explicitly_specialized<int> { using type = int; }; ot_int x; // 使用别名定义对象
疑问点:
- 这种写法是合法的,还是属于标准不要求诊断的非法情况?
- 标准中哪部分明确了using语句中的模板特化何时触发隐式实例化?
3. 标准依据与结论
合法性结论
这种写法是合法的,因为顶级using别名声明不会触发模板特化的隐式实例化,后续的显式特化声明在模板特化被隐式实例化之前,完全符合标准要求。
标准依据
模板隐式实例化的触发条件:
根据C标准的[temp.inst]章节(以C20为例,[temp.inst]/1),类模板特化的隐式实例化仅在程序需要该特化的完整类型时才会发生。顶级using别名声明(using ot_int = ordinary_template<int>;)只是引入一个类型别名,此时编译器仅需确认ordinary_template<int>是一个合法类型,不需要展开模板的具体定义,因此不会触发隐式实例化。显式特化的时机要求:
标准[temp.expl.spec]/6规定:如果一个模板特化已经被隐式实例化,之后再声明其显式特化,行为是未定义的(编译器可选择诊断或不诊断)。但在第二个例子中,using ot_int = ordinary_template<int>;并未触发隐式实例化,直到ot_int x;时才会触发,而此时will_get_explicitly_specialized<int>的显式特化已经声明完成,完全符合规则。实例化传递的时机:
当ot_int x;触发ordinary_template<int>的隐式实例化时,will_get_explicitly_specialized<int>的显式特化已经存在,编译器会优先使用显式特化版本,而非主模板的隐式实例化版本,这完全符合标准逻辑。
内容的提问来源于stack exchange,提问作者acm

