MSVC模板友元函数无法访问私有成员:是否为Bug?求解决方案
问题描述
我编写了如下C++代码:
template<typename T> class Foo; template<typename T, typename U> Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs); template<typename T> class Foo { template<typename> friend class Foo; T inner; public: Foo(T i) : inner(i) {} template<typename U> friend Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs) { lhs.inner += rhs.inner; return lhs; } }; int main() { Foo<int> a = 4; Foo<unsigned> b = 5; Foo<int> c = a + b; }
这段代码在GCC和Clang中可正常编译,但在MSVC v19.37及Visual Studio 2015中编译失败,错误信息如下:
example.cpp <source>(19): error C2248: 'Foo<unsigned int>::inner': cannot access private member declared in class 'Foo<unsigned int>' <source>(12): note: see declaration of 'Foo<unsigned int>::inner' <source>(26): note: see declaration of 'Foo<unsigned int>' <source>(27): note: see reference to function template instantiation 'Foo<int> operator +<unsigned int>(Foo<int>,const Foo<unsigned int> &)' being compiled <source>(27): note: see the first reference to 'operator +' in 'main' Compiler returned: 2
请问这是MSVC的问题吗?有没有解决方法?或者我哪里写得不对?
原因分析
这是MSVC对模板友元的处理存在兼容性问题。你已经声明了template<typename> friend class Foo;,理论上所有Foo特化版本之间应该互为友元,可访问彼此的私有成员。但MSVC在处理全局友元函数模板访问另一个特化类的私有成员时,没有正确识别这种跨特化的友元关系。
具体来说,当operator+<int, unsigned>被实例化时,它试图访问Foo<unsigned>的inner成员,但MSVC未认可Foo<int>与Foo<unsigned>的友元关系,因此抛出访问权限错误。
解决方法
以下是几种可行的修复方案:
方案1:调整友元函数的声明与定义位置
将operator+的定义移到类外,同时在类内明确关联全局的函数模板:
template<typename T> class Foo; template<typename T, typename U> Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs); template<typename T> class Foo { template<typename> friend class Foo; T inner; public: Foo(T i) : inner(i) {} // 明确关联全局的operator+模板 template<typename U, typename V> friend Foo<U> operator+(Foo<U> lhs, const Foo<V>& rhs); }; // 类外定义operator+ template<typename T, typename U> Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs) { lhs.inner += rhs.inner; return lhs; } int main() { Foo<int> a = 4; Foo<unsigned> b = 5; Foo<int> c = a + b; }
方案2:给友元函数模板添加精确的特化声明
在Foo<T>内部,针对当前T声明对应的operator+模板为友元:
template<typename T> class Foo; template<typename T, typename U> Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs); template<typename T> class Foo { template<typename> friend class Foo; T inner; public: Foo(T i) : inner(i) {} // 针对当前T,声明operator+<T, U>为友元 template<typename U> friend Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs); }; // 类外定义operator+ template<typename T, typename U> Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs) { lhs.inner += rhs.inner; return lhs; } int main() { Foo<int> a = 4; Foo<unsigned> b = 5; Foo<int> c = a + b; }
方案3:改用成员函数形式实现operator+
把operator+作为成员函数,利用已有的跨特化友元关系直接访问私有成员:
template<typename T> class Foo { template<typename> friend class Foo; T inner; public: Foo(T i) : inner(i) {} template<typename U> Foo<T> operator+(const Foo<U>& rhs) const { // 利用友元关系直接访问rhs.inner return Foo<T>(inner + rhs.inner); } }; int main() { Foo<int> a = 4; Foo<unsigned> b = 5; Foo<int> c = a + b; }
这种方式避免了全局函数模板的友元识别问题,同时保留了跨类型相加的功能。
内容的提问来源于stack exchange,提问作者PitaJ
相关产品推荐
相关产品推荐

