CRTP模式下如何限制Base类仅访问Derived类的指定私有成员
CRTP模式下的细粒度访问控制方案
需求
- Base类无法被直接实例化
- Derived类的部分方法仅能通过Base类的接口调用
- Base类仅能访问Derived类的部分私有成员
常规方案的问题
常规实现中:
- 将Base的构造函数设为protected,可满足“无法实例化Base”的需求
- 将Derived的目标方法设为私有并声明Base为friend,可满足“仅通过Base接口调用”的需求,但会导致Base能访问Derived的所有私有成员,不符合第三个需求。
常规方案代码示例:
#include <iostream> template <class T> class Base { protected: Base() = default; public: void interface() const { static_cast<const T*>(this)->do_something(); } // 此处不应通过编译 void interface_to_private() const { static_cast<const T*>(this)->do_something_private(); } }; class Derived : public Base<Derived> { friend class Base<Derived>; void do_something() const { do_something_private(); } // 这些成员不应被Base<Derived>访问 int i = 1; void do_something_private() const { std::cout << "hello " << i << std::endl; } }; int main() { Derived d; d.interface(); d.interface_to_private(); // 此处应报错 return 0; }
我们期望Base<Derived>仅能访问do_something(),而无法触及do_something_private()和int i。已知Attorney-Client idiom可解决friend访问粒度问题,但不确定如何与CRTP结合,且不想使用虚函数(这是选择CRTP的核心原因)。
现有多重继承方案
我自己通过多重继承实现了一个可行方案,但多重继承通常不被推荐,想了解是否有更优的实现方式:
#include <iostream> template <class T> class Base { protected: Base() = default; public: void interface() const { static_cast<const T*>(this)->do_something(); } // 此处不应通过编译 void interface_to_private() const { static_cast<const T*>(this)->do_something_private(); } }; class Implementation { int i = 1; void do_something_private() const { std::cout << "hello " << i << std::endl; } protected: Implementation() = default; public: void do_something() const { do_something_private(); } }; class Derived : public Base<Derived>, private Implementation { friend class Base<Derived>; }; int main() { Derived d; d.interface(); // 取消注释会触发编译错误 // d.interface_to_private(); return 0; }
更优方案:结合Attorney-Client Idiom与CRTP
无需依赖多重继承,通过引入一个代理类(Attorney)来精准控制Base能访问的Derived成员:
#include <iostream> template <class Derived> class Base; // 代理类:仅暴露允许Base访问的Derived成员 template <class Derived> class Attorney { private: // 仅包装允许Base访问的方法 static void do_something(const Derived& d) { d.do_something(); } // 仅让对应的Base类成为友元 friend class Base<Derived>; }; template <class Derived> class Base { protected: Base() = default; public: void interface() const { // 通过代理类访问Derived的成员 Attorney<Derived>::do_something(static_cast<const Derived&>(*this)); } // 此处会编译失败:Attorney未暴露do_something_private void interface_to_private() const { Attorney<Derived>::do_something_private(static_cast<const Derived&>(*this)); } }; class Derived : public Base<Derived> { private: int i = 1; void do_something_private() const { std::cout << "hello " << i << std::endl; } void do_something() const { do_something_private(); } // 仅让代理类成为友元,而非直接开放给Base friend class Attorney<Derived>; }; int main() { Derived d; d.interface(); // 取消注释会触发编译错误 // d.interface_to_private(); return 0; }
方案优势
- 无多重继承:避免了多重继承可能带来的歧义、内存布局复杂度等问题
- 精准访问控制:通过Attorney类严格限制Base能访问的成员,完全满足三个需求:
- Base构造函数为protected,无法直接实例化
- Derived的
do_something()是私有成员,仅能通过Base的interface接口调用 - Base无法访问Derived的
do_something_private()和int i,完全符合细粒度访问要求
内容的提问来源于stack exchange,提问作者francesco
相关产品推荐
相关产品推荐

