【问题标题】:Pointer to a member's member指向成员的成员的指针
【发布时间】:2020-11-19 16:49:09
【问题描述】:

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

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

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

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

【问题讨论】:

  • 使用适当的类型和正确的语法:int A::B::*p = &A::B::j;。 (你的第一个例子也不正确。)
  • @molbdnilo 但似乎他试图针对 A 而不是 B 指向 j。
  • @Kostas ...虽然我不确定这是否被允许。
  • 如果@Kostas 怀疑是正确的,那么不,“指向 T 成员的指针,其中 T 的实例是 U 的成员”不能用 C++ 表示。 (不管你在A里面还是外面定义B都是一样的情况。)

标签: c++ c++11 pointer-to-member


【解决方案1】:

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

你声明了一个嵌套类类型在这里并不是特殊情况:你可以提取一个指针A::B的数据成员并将指向数据成员的指针应用到b成员(类型为A::B)一个A 对象:

#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
}

【讨论】:

  • 知道如何定义嵌套指针很有用,但这不允许定义可变深度的指针
【解决方案2】:

为了表明我想要的在 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

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

【讨论】:

    猜你喜欢
    • 2013-05-02
    • 2018-03-24
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多