成员指向数组元素的指针

时间:2009-03-23 18:26:21

标签: c++ class pointers

可以定义指向成员的指针,稍后再使用它:

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

int main() {
foo bar; int foo::* aptr=&foo::a; bar.a=1; std::cout << bar.*aptr << std::endl; }

现在我需要一个指向数组特定元素的指针,所以通常我会写一个 int foo::* bptr=&(foo::b[0]);
但是,编译器只是抱怨"invalid use of non-static data member 'foo::b'“ 是否可以这样做(或者至少没有工会)?

编辑:我需要一个指向数组特定元素的指针,因此int foo::* ptr指向数组的第二个元素(foo::b[1])。

又一个编辑:我需要通过bar.*ptr=2访问数组中的元素,因为指针在其他地方被使用,因此无法使用{{1}调用它}或bar.*ptr[1]=2

6 个答案:

答案 0 :(得分:5)

  

然而,编译器只是抱怨“无效使用非静态数据成员'foo :: b'”

这是因为foo::afoo::b有不同的类型。更具体地说,foo::bint s的大小为2的数组。你的指针声明必须兼容,即:

int (foo::*aptr)[2]=&foo::b;
  

是否可以这样做(或者至少没有工会)?

是的,见下文:

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

int main()
{

  foo bar;

  int (foo::*aptr)[2]=&foo::b;
  /* this is a plain int pointer */
  int *bptr=&((bar.*aptr)[1]);

  bar.a=1; 
  bar.b[0] = 2;
  bar.b[1] = 11;

  std::cout << (bar.*aptr)[1] << std::endl;
  std::cout << *bptr << std::endl;
}

根据OP的要求更新了帖子。

答案 1 :(得分:3)

问题是,访问数组中的项是访问普通int的另一个间接级别。如果该数组是指针而不是您希望能够通过成员指针访问int。

struct foo
{
  int a;
  int *b;
};

int main()
{

  foo bar;
  int foo::* aptr=&(*foo::b); // You can't do this either!
  bar.a=1;
  std::cout << bar.*aptr << std::endl;
}

您可以做的是定义返回所需int的成员函数:

struct foo
{
  int a;
  int *b;
  int c[2];

  int &GetA() { return a; } // changed to return references so you can modify the values
  int &Getb() { return *b; }
  template <int index>
  int &GetC() { return c[index]; }
};
typedef long &(Test::*IntAccessor)();

void SetValue(foo &f, IntAccessor ptr, int newValue)
{  
    cout << "Value before: " << f.*ptr();
    f.*ptr() = newValue;
    cout << "Value after: " << f.*ptr();
}

int main()
{
  IntAccessor aptr=&foo::GetA;
  IntAccessor bptr=&foo::GetB;
  IntAccessor cptr=&foo::GetC<1>;

  int local;
  foo bar;
  bar.a=1;
  bar.b = &local;
  bar.c[1] = 2;

  SetValue(bar, aptr, 2);
  SetValue(bar, bptr, 3);
  SetValue(bar, cptr, 4);
  SetValue(bar, &foo::GetC<0>, 5);
}

然后你至少有一个一致的界面,允许你在foo上改变不同的值。

答案 2 :(得分:1)

你不能用语言本身做到这一点。但你可以提升。将一个仿函数绑定到该数组的某个元素并将其分配给boost::function

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/function.hpp>
#include <iostream>

struct test {
    int array[3];
};

int main() {
    namespace lmb = boost::lambda;

    // create functor that returns test::array[1]
    boost::function<int&(test&)> f;
    f = lmb::bind(&test::array, lmb::_1)[1];

    test t = {{ 11, 22, 33 }};
    std::cout << f(t) << std::endl; // 22

    f(t) = 44;
    std::cout << t.array[1] << std::endl; // 44
}

答案 3 :(得分:1)

2020年更新,带有实际解决方案:

  • 该标准目前未指定任何实际使用成员指针的方式,该方式允许算术或其他任何方式将指针获取到“内部”数组元素
  • OTOH,标准库现在具有所有必要的知识,可以自行修补适当的成员指针类,甚至可以访问数组元素。

首先,成员指针通常被实现为“只是偏移量”,尽管这很可怕。让我们看一个例子(在g ++ 9上,arch amd64):

struct S { int a; float b[10]; };

float(S::*mptr)[10] = &S::b;
*reinterpret_cast<uintptr_t *>(&mptr)  //this is 4

int S::*iptr = &S::a;
*reinterpret_cast<uintptr_t *>(&iptr)  //this is 0

iptr = nullptr;
*reinterpret_cast<uintptr_t *>(&iptr)  //this seems to be 18446744073709551615 on my box

相反,您可以做一些包装(虽然很长,但是我不想删除便捷运算符):

#include <type_traits>

template<class M, typename T>
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<M, T>& p)
    { return (*a)->*p; }
    friend T& operator->*(M* a, const member_ptr<M, T>& p)
    { return (*a)->*p; }

    /* operator.* cannot be overloaded, so just take the arrow again */
    friend const T& operator->*(const M& a, const member_ptr<M, T>& p)
    { return *reinterpret_cast<const T*>(reinterpret_cast<const char*>(&a) + p.off_); }
    friend T& operator->*(M& a, const member_ptr<M, T>& p)
    { return *reinterpret_cast<T*>(reinterpret_cast<char*>(&a) + p.off_); }

    /* convert array access to array element access */
    member_ptr<M, typename std::remove_extent<T>::type> operator*() const
    { return member_ptr<M, typename std::remove_extent<T>::type>(off_); }

    /* the same with offset right away */
    member_ptr<M, typename std::remove_extent<T>::type> operator[](size_t offset) const
    { return member_ptr<M, typename std::remove_extent<T>::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<class M, typename T>
member_ptr<M, T> make_member_ptr(T M::*a)
{ return member_ptr<M, T>(reinterpret_cast<uintptr_t>(&(((M*)nullptr)->*a)));}

现在我们可以直接将指针指向数组元素:

auto mp = make_member_ptr(&S::b)[2];
S s;
s->*mp = 123.4;

// s.b[2] is now expectably 123.4

最后,如果您真的非常喜欢物化引用,则可能会有点草率,并使它们组成:

// in class member_ptr, note transitivity of types M -> T -> TT:
    template<class TT>
    member_ptr<M,TT> operator+(const member_ptr<T,TT>&t)
    { return member_ptr<M,TT>(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

答案 4 :(得分:0)

  typedef int (foo::*b_member_ptr)[2];
  b_member_ptr c= &foo::b;

一切正常。

成员和函数指针用法的小技巧 试着写

char c = &foo::b; // or any other function or member pointer

并且在compiller错误中,您会看到预期的类型,对于您的案例int (foo::*)[2]

修改
如果没有这个指针,我不确定你想要的是合法的。为了向指针添加1个偏移量,您应该从成员数组上的指针获取数组上的指针。但是如果没有这个,你可以取消引用成员指针。

答案 5 :(得分:0)

我不确定这是否适合你,但我想做类似的事情并通过从另一个方向解决问题来解决这个问题。在我的班级中,我有几个对象,我想通过命名标识符访问或循环迭代。我没有创建指向数组中某些对象的成员指针,而是单独声明了所有对象,并创建了一个静态的成员指针数组。

像这样:

struct obj
{
    int somestuff;
    double someotherstuff;
};

class foo
{
  public:
    obj apples;
    obj bananas;
    obj oranges;

    static obj foo::* fruit[3];

    void bar();
};

obj foo::* foo::fruit[3] = { &foo::apples, &foo::bananas, &foo::oranges };


void foo::bar()
{
    apples.somestuff = 0;
    (this->*(fruit[0])).somestuff = 5;
    if( apples.somestuff != 5 )
    {
        // fail!
    }
    else
    {
        // success!
    }
}



int main()
{
    foo blee;
    blee.bar();
    return 0;
}

这似乎对我有用。我希望有所帮助。