这个技巧首次由 Johannes Schaub(litb)在 2010 年的一篇文章 Access to private members. That's easy! 中提出。
首先需要搞懂几个概念:
模板的概念(特别:模板显示实例化)
template <typename T> struct X { ... }; // 主模板 template <typename T> struct X<T*> { ... }; // 偏特化 template <> struct X<int> { ... }; // 显式全特化 template struct X<int>; // 显式实例化 ★ X<int> obj; // 隐式实例化 extern template struct X<int>; // 显式实例化声明(抑制隐式实例化)直接给案例,我也没看懂
#include <iostream> // ============ 目标类:有私有成员 ============ class Victim { private: int secret_ = 42; void sayHi() { std::cout << "Hi, secret=" << secret_ << "\n"; } }; // ============ 通用 "Rob" 模板 ============ // Tag 是一个空标签类型,用来区分不同的被偷成员 // Tag::type 是被偷成员的"成员指针类型" // M 是那个成员指针的具体值(作为非类型模板参数传入) template <typename Tag, typename Tag::type M> struct Rob { // friend 函数定义:在这里定义 get(Tag),返回成员指针 M // 注意:这个 friend 函数的定义只有当 Rob 被实例化时才产生 friend typename Tag::type get(Tag) { return M; } }; // ============ 为 secret_ 定义一个 Tag ============ struct Victim_secret { using type = int Victim::*; // 数据成员指针类型 friend type get(Victim_secret); // 声明 friend 函数(无定义) }; // 显式实例化 Rob,触发 friend 函数 get(Victim_secret) 的定义 // 这一行是关键:&Victim::secret_ 作为模板参数在此上下文中不做访问检查 template struct Rob<Victim_secret, &Victim::secret_>; // ============ 为 sayHi 定义一个 Tag ============ struct Victim_sayHi { using type = void (Victim::*)(); // 成员函数指针类型 friend type get(Victim_sayHi); }; template struct Rob<Victim_sayHi, &Victim::sayHi>; // ============ 使用 ============ int main() { Victim v; // 偷数据成员 auto secretPtr = get(Victim_secret{}); // 得到 int Victim::* std::cout << "secret = " << v.*secretPtr << "\n"; // 读 v.*secretPtr = 100; // 写 std::cout << "secret = " << v.*secretPtr << "\n"; // 偷成员函数 auto sayHiPtr = get(Victim_sayHi{}); (v.*sayHiPtr)(); // 调用私有函数 return 0; }