You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现接收类及类成员指针的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> {};
};

关键点说明

  1. 模板参数Vector3d T::* MemPtr直接约束传入的必须是类T的Vector3d类型成员指针,编译期即可校验类型合法性。
  2. 在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> {};
};

关键点说明

  1. 用auto作为模板参数,让编译器自动推导成员指针的类型,配合Vector3dMemberPointer概念,既能保证类型安全,又提升了模板的通用性。
  2. 使用方式和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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 06:44:55