如何实现接收类及类成员指针的C++ Accessor模板?(C++17/20)
C17与C20下Accessor模板的实现方案
核心需求回顾
我们需要实现一个Accessor模板,关联目标类T及其Vector3d类型的成员P,无需持有对象引用(依赖getObject<T>获取实例),且使用时尽可能简洁(偏好Accessor<&one_point>这类省略类作用域的写法)。
C++17实现方案
在C++17中,可直接将成员指针作为非类型模板参数传入,利用类作用域简化成员指针的写法(类内部的&one_point等价于&Accessed::one_point)。
完整代码
#include <type_traits> // 假设Vector3d来自Eigen,或自定义实现 #include <Eigen/Core> using Vector3d = Eigen::Vector3d; // 外部提供的对象获取函数,无需关注实现细节 template<typename T> T& getObject(); // Accessor模板:接受Vector3d类型的类成员指针作为参数 template<Vector3d T::* MemPtr> class Accessor { public: // 从成员指针中提取所属类的类型 using OwnerType = T; OwnerType& setMember(const Vector3d& point) { OwnerType& obj = getObject<OwnerType>(); obj.*MemPtr = point; return obj; } }; // 使用示例 class Accessed { Vector3d one_point; Vector3d another_point; public: // 在类作用域内,&one_point自动解析为&Accessed::one_point class OneAccessor : public Accessor<&one_point> {}; class AnotherAccessor : public Accessor<&another_point> {}; };
关键点说明
- 模板参数
Vector3d T::* MemPtr直接约束传入的必须是类T的Vector3d类型成员指针,编译期即可校验类型合法性。 - 在
Accessed类内部使用时,&one_point会被编译器自动解析为&Accessed::one_point,无需手动添加类作用域前缀。
C++20实现方案
C++20提供了**概念(Concepts)**和更灵活的auto非类型模板参数,可写出更通用、约束更清晰的实现,同时保持简洁的使用方式。
完整代码
#include <concepts> #include <type_traits> #include <Eigen/Core> using Vector3d = Eigen::Vector3d; template<typename T> T& getObject(); // 自定义概念:约束模板参数必须是指向Vector3d类型成员的指针 template<typename MemPtrType> concept Vector3dMemberPointer = requires(MemPtrType ptr) { // 要求成员指针能提取出所属类的类型 typename std::remove_pointer_t<MemPtrType>::class_type; // 要求成员的类型必须是Vector3d requires std::is_same_v< std::remove_reference_t<decltype(std::declval<typename std::remove_pointer_t<MemPtrType>::class_type>().*ptr)>, Vector3d >; }; // Accessor模板:用auto接受任意成员指针,再通过Concept约束类型 template<auto MemPtr> requires Vector3dMemberPointer<decltype(MemPtr)> class Accessor { public: using OwnerType = typename std::remove_pointer_t<decltype(MemPtr)>::class_type; OwnerType& setMember(const Vector3d& point) { OwnerType& obj = getObject<OwnerType>(); obj.*MemPtr = point; return obj; } }; // 使用方式与C++17完全一致 class Accessed { Vector3d one_point; Vector3d another_point; public: class OneAccessor : public Accessor<&one_point> {}; class AnotherAccessor : public Accessor<&another_point> {}; };
关键点说明
- 用
auto作为模板参数,让编译器自动推导成员指针的类型,配合Vector3dMemberPointer概念,既能保证类型安全,又提升了模板的通用性。 - 使用方式和C++17完全相同,依然支持类内部省略类作用域的写法。
关于“无需&的写法”的说明
你提到的Accessor<one_point>这类不带&的写法,在C++中无法直接实现:
- 非静态成员变量
one_point本身不是常量表达式,不符合非类型模板参数的要求。 - 唯一的变通方式是用宏封装,需要显式指定所属类,示例如下:
此宏会自动生成#define DEFINE_ACCESSOR(OWNER_CLASS, MEMBER_NAME) \ class MEMBER_NAME##Accessor : public Accessor<&OWNER_CLASS::MEMBER_NAME> {}; // 在Accessed类内部使用 class Accessed { Vector3d one_point; Vector3d another_point; public: DEFINE_ACCESSOR(Accessed, one_point); DEFINE_ACCESSOR(Accessed, another_point); };one_pointAccessor和another_pointAccessor类,减少手动书写的冗余。
内容的提问来源于stack exchange,提问作者André Caldas
相关产品推荐
相关产品推荐

