Member pointer to array element

前端 未结 6 1226
野的像风
野的像风 2021-02-04 06:29

It\'s possible to define a pointer to a member and using this later on:

struct foo
{
  int a;
  int b[2];
};

int main() {
foo bar; int foo::*

6条回答
  •  悲&欢浪女
    2021-02-04 06:37

    2020 update, with actual solution:

    • The Standard does currently not specify any way to actually work with the member pointers in a way that would allow arithmetics or anything to get the pointer to the "inner" array element
    • OTOH, the standard library now has all the necessities to patch the appropriate member pointer class yourself, even with the array element access.

    First, the member pointers are usually implemented as "just offsets", although quite scary. Let's see an example (on g++9, arch amd64):

    struct S { int a; float b[10]; };
    
    float(S::*mptr)[10] = &S::b;
    *reinterpret_cast(&mptr)  //this is 4
    
    int S::*iptr = &S::a;
    *reinterpret_cast(&iptr)  //this is 0
    
    iptr = nullptr;
    *reinterpret_cast(&iptr)  //this seems to be 18446744073709551615 on my box
    

    Instead you can make a bit of a wrapper (it's quite long but I didn't want to remove the convenience operators):

    #include 
    
    template
    class member_ptr
    {
        size_t off_;
    public:
        member_ptr() : off_(0) {}
        member_ptr(size_t offset) : off_(offset) {}
    
        /* member access */
        friend const T& operator->*(const M* a, const member_ptr& p)
        { return (*a)->*p; }
        friend T& operator->*(M* a, const member_ptr& p)
        { return (*a)->*p; }
    
        /* operator.* cannot be overloaded, so just take the arrow again */
        friend const T& operator->*(const M& a, const member_ptr& p)
        { return *reinterpret_cast(reinterpret_cast(&a) + p.off_); }
        friend T& operator->*(M& a, const member_ptr& p)
        { return *reinterpret_cast(reinterpret_cast(&a) + p.off_); }
    
        /* convert array access to array element access */
        member_ptr::type> operator*() const
        { return member_ptr::type>(off_); }
    
        /* the same with offset right away */
        member_ptr::type> operator[](size_t offset) const
        { return member_ptr::type>(off_)+offset; }
    
        /* some operators */
        member_ptr& operator++()
        { off_ += sizeof(T); return *this; };
        member_ptr& operator--()
        { off_ -= sizeof(T); return *this; };
        member_ptr operator++(int)
        { member_ptr copy; off_ += sizeof(T); return copy; };
        member_ptr operator--(int)
        { member_ptr copy; off_ -= sizeof(T); return copy; };
    
        member_ptr& operator+=(size_t offset)
        { off_ += offset * sizeof(T); return *this; }
        member_ptr& operator-=(size_t offset)
        { off_ -= offset * sizeof(T); return *this; }
        member_ptr operator+(size_t offset) const
        { auto copy = *this; copy += offset; return copy; }
        member_ptr operator-(size_t offset) const
        { auto copy = *this; copy -= offset; return copy; }
    
        size_t offset() const { return off_; }
    };
    
    template
    member_ptr make_member_ptr(T M::*a)
    { return member_ptr(reinterpret_cast(&(((M*)nullptr)->*a)));}
    

    Now we can make the pointer to the array element directly:

    auto mp = make_member_ptr(&S::b)[2];
    S s;
    s->*mp = 123.4;
    
    // s.b[2] is now expectably 123.4
    

    Finally, if you really, really like materialized references, you may get a bit haskell-lensish and make them compose:

    // in class member_ptr, note transitivity of types M -> T -> TT:
        template
        member_ptr operator+(const member_ptr&t)
        { return member_ptr(off_ + t.offset()); }
    
    // test:
    struct A { int a; };
    struct B { A arr[10]; };
    
    B x;
    auto p = make_member_ptr(&B::arr)[5] + make_member_ptr(&A::a)
    
    x->*p = 432.1;
    // x.arr[5].a is now expectably 432.1
    

提交回复
热议问题