C++访问类私有成员时友元函数get的声明作用及访问规则疑问
C++私有成员访问技巧:友元函数可见性解析
先看这段用于访问类私有成员的C++代码:
#include <iostream> struct A { private: int member; }; template<typename Tag, typename Tag::type M> struct Rob { friend typename Tag::type get(Tag) { return M; } }; // 用于访问A::member的标签结构体 struct A_member { typedef int A::*type; friend type get(A_member); }; template struct Rob<A_member, &A::member>; int main() { A a; a.*(get(A_member())) = 42; // 向私有成员写入42 std::cout << "proof: " << a.*get(A_member()) << std::endl; }
核心疑问
按照cppreference的说明,Rob结构体的显式实例化已经把get函数定义为全局命名空间的非成员函数,为什么main里直接访问不到?移除A_member中的friend type get(A_member);声明后编译报错,这个声明到底起什么作用?
关键原理:C++友元函数的名称查找规则
这里的核心是友元函数的可见性规则和依赖于参数的查找(ADL):
- 当在模板结构体
Rob内部定义friend get函数时,虽然这个函数最终是全局命名空间的非成员函数,但它的名称不会自动进入全局命名空间的普通查找范围——友元函数的定义不会主动把名称“暴露”到外围命名空间,除非有对应的外部声明。 A_member里的friend type get(A_member);声明,是给ADL留的“入口”:- 它在
A_member的作用域内声明了get函数的存在; - 当main里调用
get(A_member())时,因为传入的参数是A_member类型,编译器会触发ADL——自动去参数类型的作用域里查找匹配的函数声明,从而找到那个由Rob显式实例化生成的全局get函数。
- 它在
- 如果移除这个友元声明,main里调用
get时,普通全局查找找不到它,ADL也找不到对应的函数声明,编译器自然会报错说get未定义。
这个技巧本质是利用了:模板显式实例化会强制生成友元函数的定义,加上ADL的特性,绕开了私有成员的访问限制——而那个友元声明就是让外部代码能通过ADL找到这个“隐藏”的全局友元函数的关键。
内容的提问来源于stack exchange,提问作者Monte
相关产品推荐
相关产品推荐

