非模板类静态模板成员与自由模板函数作为友元的编译差异探究
为什么Clang对类静态模板成员和自由模板函数的友元处理不一样?
先给大家看个有意思的代码问题:这段代码在GCC、ICC和最新版MSVC里都能正常编译运行,但到了Clang这儿,就会在标记的那行报错,说调用了B<int>的私有构造函数,可下面的自由模板函数调用却完全没问题。
struct A { template<class T> static void create () { T(); } }; template<class T> void create() { T(); } template<typename T> struct B { friend void A::create<B>(); friend void create<B>(); private: B() = default; }; int main() { A::create<B<int>>(); // Clang 在这里报错:调用了B<int>的私有构造函数 create<B<int>>(); // 完全正常! }
那为啥会出现这种差异呢?核心原因在于C++编译器对两种友元声明的解析规则不一样,尤其是Clang的处理逻辑和其他几个编译器有区别:
1. 自由模板函数的友元声明:正确匹配模板特化
当我们在B<T>里写friend void create<B>();时,这里的B其实是当前模板实例B<T>的缩写。这个声明会被编译器正确解析为:将全局模板create的特化版create<B<T>>()声明为B<T>的友元。
当B<int>实例化时,编译器会自动关联到全局模板create对应的特化,确认这个特化是B<int>的友元,所以调用create<B<int>>()时能顺利访问私有构造函数。
2. 类静态模板成员的友元声明:Clang的解析分歧
而对于friend void A::create<B>();这个声明,问题就出在Clang对它的解读上:
- GCC、MSVC等编译器会把它解析为:将类
A的静态模板成员create的特化版A::create<B<T>>()声明为B<T>的友元,不管这个特化有没有提前实例化,都认可友元关系。 - 但Clang在这里的处理更严格:它认为这个声明是在尝试声明一个非模板函数
A::create<B<T>>()作为友元,但实际上A::create是个模板,对应的特化此时还没被实例化出来,所以Clang判定这个友元声明没有匹配到实际存在的函数,自然不会授予访问私有构造的权限,于是就报错了。
简单来说,就是Clang对类静态模板成员的友元特化声明的解析逻辑,和其他主流编译器存在分歧,导致了这个兼容性问题。
内容的提问来源于stack exchange,提问作者SergeyA
相关产品推荐
相关产品推荐

