在c++标准中,模板的显式实例化定义忽略成员访问说明符,因此可以利用这一特性使用模板来实现访问其他任何类的私有成员变量
参考:
https://en.cppreference.com/w/cpp/language/class_template
https://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends
https://github.com/lackhole/Lupin
实现模板代码
#include <utility>
namespace Access {
/// Explicit instantiation definitions ignore member access specifiers:
/// parameter types and return types may be private
template<typename Tag, auto Member>
struct Accessor;
template<typename Tag, typename _Cls, typename T, T _Cls::*Member>
struct Accessor<Tag, Member> {
typedef T ValueType;
const T &get(const _Cls &obj) {
return obj.*Member;
}
void set(_Cls &obj, T value) {
obj.*Member = std::move(value);
}
friend auto getAccessorTypeImpl(Tag) { return Accessor{}; }
};
template<typename Tag>
struct TagBase {
friend auto getAccessorTypeImpl(Tag);
};
template<typename Accessor>
struct AccessorTrait {
using AccessorType = Accessor;
using ValueType = typename Accessor::ValueType;
};
template<typename Tag>
struct TagTrait {
using TagType = Tag;
using AccessorType = decltype(getAccessorTypeImpl(std::declval<TagType>()));
using ValueType = typename AccessorTrait<AccessorType>::ValueType;
};
template<typename Tag, typename _Cls, typename Accessor = typename TagTrait<Tag>::AccessorType>
auto get(const _Cls &obj) -> const typename AccessorTrait<Accessor>::ValueType & {
return Accessor{}.get(obj);
}
template<typename Tag,
typename _Cls,
typename Accessor = typename TagTrait<Tag>::AccessorType,
typename T>
void set(_Cls &obj, T &&value) {
/// typename AccessorTrait<Accessor>::ValueType
return Accessor{}.set(obj, std::forward<T>(value));
}
}
使用如下
#include <cassert>
class AnyObject {
int m_Data;
public:
AnyObject() : m_Data(42) {}
void assertValue(int v) {
assert(v == m_Data);
}
};
struct AnyObject_m_Data_Tag : Access::TagBase<AnyObject_m_Data_Tag> {};
template struct Access::Accessor<AnyObject_m_Data_Tag, &AnyObject::m_Data>; /// 显式实例化
int main() {
AnyObject object;
/// get
int m_Data = Access::get<AnyObject_m_Data_Tag>(object);
object.assertValue(m_Data);
/// set
Access::set<AnyObject_m_Data_Tag>(object, 24);
object.assertValue(24);
return 0;
}
在上面的代码中,先声明一个Tag用来标识某类中的私有成员,再用template struct Access::Accessor<AnyObject_m_Data_Tag, &AnyObject::m_Data>; 显示实例化Accessor,即访问者的实现,此时传入私有成员变量指针&AnyObject::M_Data是不会有编译错误的。
接下来就可以通过Tag来获取这个特化Accessor类的实例,进行私有成员变量的访问。
其中Tag与Accessor的关联可以通过全局的友元函数来实现