C++ 使用模板访问任何类的私有成员变量

文章介绍了一种C++技巧,通过模板的显式实例化定义来规避成员访问说明符的限制,从而能够从外部访问其他类的私有成员变量。具体实现包括定义一个Accessor模板结构体,然后通过Tag标识特定类的私有成员,并显式实例化该模板,最后通过Tag获取Accessor实例来读写私有成员。这种方法利用了模板特化和友元函数来实现。
摘要由CSDN通过智能技术生成

在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的关联可以通过全局的友元函数来实现

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值