简体   繁体   English

指向成员的成员的指针

[英]Pointer to a member's member

C++ allows one to define pointers to members of a class like: C++ 允许定义指向类成员的指针,例如:

struct A
{
  int i;
};

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

but what if I want a pointer to a member that's on a deeper "level" like this?但是如果我想要一个指向像这样更深“级别”的成员的指针怎么办?

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

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

Theoretically it seems that if pointer to members are compiled to offets from the start of the object this could have been easily supported by the language although things like virtual/diamond inheritance would probably make this far too complicated从理论上讲,如果指向成员的指针从对象的开始被编译为偏移,这可能很容易被语言支持,尽管诸如虚拟/钻石继承之类的东西可能会使这变得过于复杂

What's the easiest way to achieve that without performance penalties or undefined behaviour?在没有性能损失或未定义行为的情况下实现这一目标的最简单方法是什么?

My first idea was to just use an offset and work with raw pointers on the object but that seems like it might not be defined behaviour and would also make it impossible for the compiler to detect if I'm pointing to actual fields with the correct type我的第一个想法是只使用偏移量并使用对象上的原始指针,但这似乎不是定义的行为,并且也会使编译器无法检测我是否指向具有正确类型的实际字段

You can only define a pointer to data member for a given class for actual data members of that class.您只能为给定类的实际数据成员定义指向数据成员的指针。

That you have a nested class type declared is no special case here: you can extract a pointer a data member of A::B and apply the pointer to data member onto the b member (of type A::B ) of an A object:您声明了一个嵌套的类类型并不是特例:您可以提取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
}

However if you want to apply a pointer to data member on an A object, the best you can get is a pointer to the data member b .但是,如果您想在A对象上应用指向数据成员的指针,那么您可以获得的最好的方法是指向数据成员b的指针。

You could always use nested pointer to data member pointers for a nested class objects:对于嵌套类对象,您始终可以使用指向数据成员指针的嵌套指针:

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
}

Just for the sake of showing that what I want isn't impossible in C++ I'll provide following solution:只是为了表明我想要的在 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;
    }
};

Sample usage code:示例使用代码:

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);
}

ptr and ptr2 are variables of the same type despite refering to members in different depth, just as I want. ptrptr2是相同类型的变量,尽管引用了不同深度的成员,就像我想要的那样。

If executed the program prints如果执行程序打印

Jim
Street

Now the only problem with this solution is that it's not really simple and also not very performant given that it requires rather complicated variable definitions and needs polymorphism to differentiate on different paths现在这个解决方案的唯一问题是它不是很简单而且性能也不是很好,因为它需要相当复杂的变量定义并且需要多态来区分不同的路径

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

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