问询C++标准中显式特化模板实参列表的访问豁免规则依据
先从你已经明确的显式实例化规则说起:[temp.explicit]/12有明确规定:
常规访问检查规则不适用于指定显式实例化时使用的名称。[...]
这就是为什么你写出的显式实例化代码是合法的:
template<typename T, typename U> class A {}; class B { class C {}; enum class E {}; }; // OK: 显式实例化定义 template class A<B::C, B::E>;
接下来针对你关心的两种特化情况逐一分析:
显式(全)特化:编译器共识背后的标准逻辑
虽然[temp.expl.spec](显式特化的条款)里没有直接复制[temp.explicit]/12的表述,但我们可以从标准的意图和隐含规则来理解为什么GCC和Clang都接受显式全特化的代码:
显式全特化是针对某个具体模板实例的自定义定义,它的声明本质上是在为一个特定的实例提供替代实现,而不是定义一个新的模板。从标准的设计逻辑来看,显式全特化和显式实例化都是针对特定实例的操作——前者是自定义实现,后者是强制生成实例,两者在访问检查上应该保持一致的豁免规则。
此外,从编译器的实现共识(两大主流编译器都接受template<> class A<B::C, B::E> {};)也能反推,这种用法是符合标准意图的,即使没有在[temp.expl.spec]中明确写出,也是隐含允许的。
部分特化:无豁免,不良构
部分特化的情况就完全不同了:部分特化本质上是一个新的模板(它不是针对某个具体实例,而是针对一组实例的模板),[temp.partial.spec]中没有任何条款豁免它的访问检查规则,因此常规的访问检查在这里完全适用。
这就是为什么Clang会报错'C' is a private member of 'B'——它的行为是符合标准的。而GCC接受该代码,属于编译器的实现扩展,并不符合标准的要求。你推测的“部分特化是不良构的”是正确的,因为默认的访问规则没有被任何条款豁免。
总结
- 显式(全)特化:虽然标准没有在对应条款中明确写出,但从设计意图和编译器共识来看,访问检查规则不适用于其模板实参列表,代码是良构的。
- 部分特化:无任何标准条款豁免访问检查,引用私有类型的部分特化属于不良构代码,Clang的报错符合标准,GCC的接受是实现扩展。
内容的提问来源于stack exchange,提问作者dfrib

