无需直接继承或显式声明,实现持有者类对成员类的独占访问
问题:如何让持有者类独占访问成员类的非公开成员(无需继承/显式友元声明)
是否存在一种方法,让持有其他类作为成员变量的类(持有者类)获得对成员类的独占访问权限,无需继承,也无需在成员类内部直接声明友元类名?
示例场景
以下代码中,B作为持有者类,无法访问成员类A的私有成员m,编译会报错:
class A { int m = 0; }; class B { public: A test1; A test2; void do_something() { test1.m = 2; } // 编译错误:无法访问A的私有成员 };
现有方案分析
1. 模板友元类方案
将成员类A定义为模板,通过模板参数指定持有者类作为友元:
template <class Friend> class A { friend Friend; int m; }; class B { void do_something() { A<B> a; a.m = 2; // 编译通过:B是A<B>的友元 } };
缺点:必须显式指定模板参数B,当持有者类有多个A成员时,每个成员都要写A<B>,代码冗余。
2. 错误的反向友元声明
原设想在持有者类中声明成员类为友元,实际无法实现需求:
class A; class B { friend class A; // 仅允许A访问B的私有成员,而非B访问A的成员 A test; void do_something() { test.m = 2; } // 仍然编译报错 };
该方案逻辑错误,friend class A的作用是让A能访问B的私有成员,而非反向授权,不符合核心诉求。
需求探索:隐式推导持有者类型
曾尝试通过工具函数利用this指针隐式传递持有者类型,但存在语法问题:
template <class Owner> constexpr auto make_A(Owner*) { return A<Owner>{}; // 原代码遗漏实例化语法 } class B { auto MyA = make_A(this); // 错误:非静态成员不能用auto默认初始化(C++17仅支持静态成员) };
思路可行但语法不合法,且未达到简化代码的目的。
满足核心诉求的解决方案
结合C++17的类模板推导指引和私有构造函数,实现隐式绑定持有者、独占访问、代码简洁的效果:
实现代码
// 辅助标签:用于推导持有者类型 template <class Owner> struct owner_tag {}; // 成员类模板:仅允许绑定的持有者类访问私有成员 template <class Owner> class A { // 私有构造函数:仅友元和工厂函数能创建实例 A(owner_tag<Owner>) {} int m = 0; // 友元授权:仅持有者类Owner能访问私有成员 friend Owner; public: // 工厂函数:通过this指针隐式推导持有者类型 template <class Derived> static A<Derived> create(Derived*) { return A<Derived>(owner_tag<Derived>{}); } }; // 类模板推导指引:让编译器自动根据标签推导Owner类型 template <class Derived> A(owner_tag<Derived>) -> A<Derived>; // 持有者类 class B { // 隐式推导成员类型为A<B>,无需显式指定模板参数 A test1 = A::create(this); A test2 = A::create(this); public: void do_something() { test1.m = 2; // 编译通过:B是A<B>的友元 test2.m = 3; } };
方案优势
- 隐式推导:通过
A::create(this)自动绑定持有者类B,成员变量直接写A即可,编译器自动推导为A<B> - 独占访问:仅持有者类能访问对应
A<Owner>的私有成员,其他类无法创建或访问A的非公开接口 - 无冗余友元声明:成员类
A无需手动添加多个友元,通过模板参数自动绑定持有者
依赖说明
需要C++17及以上版本支持类模板推导指引特性。
内容的提问来源于stack exchange,提问作者Vitaly Protasov
相关产品推荐
相关产品推荐

