繁体   English   中英

指向成员的成员的指针

[英]Pointer to a member's member

C++ 允许定义指向类成员的指针,例如:

struct A
{
  int i;
};

void a()
{
  int A::*p = &A::i;
}

但是如果我想要一个指向像这样更深“级别”的成员的指针怎么办?

struct A
{
  int i;
  struct B{int j;};
  B b;
};

void a()
{
  int A::*p = &A::b::j;
}

从理论上讲,如果指向成员的指针从对象的开始被编译为偏移,这可能很容易被语言支持,尽管诸如虚拟/钻石继承之类的东西可能会使这变得过于复杂

在没有性能损失或未定义行为的情况下实现这一目标的最简单方法是什么?

我的第一个想法是只使用偏移量并使用对象上的原始指针,但这似乎不是定义的行为,并且也会使编译器无法检测我是否指向具有正确类型的实际字段

您只能为给定类的实际数据成员定义指向数据成员的指针。

您声明了一个嵌套的类类型并不是特例:您可以提取A::B数据成员的指针,并将指向数据成员的指针应用到A对象的b成员(类型A::B )上:

#include <iostream>

struct A {
  int i;
  struct B{ int j; };
  B b;
};

int main() {
    A a{1, {42}};  // a.b.j is 42
    
    // Declare a pointer-to-data-member of B (which
    // is a nested class in A).
    int A::B::*p = &A::B::j;
    
    // Apply to an actual 'B' object to retrieve
    // the data member value pointed to.
    std::cout << a.b.*p;  // 42
}

但是,如果您想在A对象上应用指向数据成员的指针,那么您可以获得的最好的方法是指向数据成员b的指针。

对于嵌套类对象,您始终可以使用指向数据成员指针的嵌套指针:

int main() {
    A a{1, {42}};  // a.b.j is 42
    
    A::B A::*pb = &A::b;
    int A::B::*pj = &A::B::j;
    std::cout << (a.*pb).*pj;  // 42
}

只是为了表明我想要的在 C++ 中并非不可能,我将提供以下解决方案:

// Example program
#include <iostream>
#include <string>
#include <memory>

template<typename T, typename P>
class MemberPointer
{
public:
    virtual P& access(T& obj) = 0;
};

template<typename T, typename P>
class SimpleMemberPointer : public MemberPointer<T, P>
{
    P T::*ptr;
public:
    SimpleMemberPointer(P T::*ptr): ptr(ptr){}

    P& access(T& obj) override
    {
        return obj.*ptr;
    }
    
    using object = T;
    using property = P;
};

template<typename Ptr, typename P>
class NestedMemberPointer : public MemberPointer<typename Ptr::object, P>
{
public:    
    using object = typename Ptr::object;
    using property = P;
private:
    using intermediate = typename Ptr::property;

    Ptr parent;
    P intermediate::*ptr;
public:
    NestedMemberPointer(Ptr parent, P intermediate::*ptr): parent(parent), ptr(ptr){}

    P& access(object& obj) override
    {
        return parent.access(obj).*ptr;
    }
};

示例使用代码:

class Person
{
public:
    std::string name;
    
    struct Address{
        std::string street;
    }address;
};

void test(Person &p, MemberPointer<Person, std::string> &ptr)
{
    std::cout << ptr.access(p) << std::endl;
}

int main()
{
    Person p{"Jim", {"Street"}};
    
    std::unique_ptr<MemberPointer<Person, std::string>> ptr = std::make_unique<SimpleMemberPointer<Person, std::string>>(&Person::name);
    std::unique_ptr<MemberPointer<Person, std::string>> ptr2 = std::make_unique<NestedMemberPointer<SimpleMemberPointer<Person, Person::Address>, std::string>>(SimpleMemberPointer<Person, Person::Address>{&Person::address}, &Person::Address::street);
    
    test(p, *ptr);
    test(p, *ptr2);
}

ptrptr2是相同类型的变量,尽管引用了不同深度的成员,就像我想要的那样。

如果执行程序打印

Jim
Street

现在这个解决方案的唯一问题是它不是很简单而且性能也不是很好,因为它需要相当复杂的变量定义并且需要多态来区分不同的路径

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM