Член указатель на элемент массива - PullRequest
11 голосов
/ 23 марта 2009

Можно определить указатель на член и использовать это позже:

<code>struct foo
{
  int a;
  int b[2];
};</p>

<p>int main()
{<br>
  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, так как указатель используется в другом месте, поэтому его нельзя вызвать с помощью bar.*ptr[1]=2 или *ptr=2 .

Ответы [ 5 ]

5 голосов
/ 23 марта 2009

Однако компилятор просто жалуется на «недопустимое использование элемента нестатических данных 'foo :: b'"

Это потому, что foo::a и foo::b имеют разные типы. Более конкретно, foo::b - это массив размером 2 int s. Ваше объявление указателя должно быть совместимым, т.е.:

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

Обновлено сообщение с требованиями ОП.

3 голосов
/ 23 марта 2009

Проблема в том, что доступ к элементу в массиве - это еще один уровень косвенного обращения по сравнению с доступом к обычному 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.

1 голос
/ 23 марта 2009

Вы не можете сделать это из самого языка. Но вы можете с наддувом. Привязать функтор к какому-либо элементу этого массива и присвоить его 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
}
0 голосов
/ 14 августа 2009

Я не уверен, сработает ли это для вас или нет, но я хотел сделать то же самое и обойти это, подойдя к проблеме с другого направления. В моем классе у меня было несколько объектов, которые я хотел, чтобы они были доступны через именованный идентификатор или повторялись в цикле. Вместо того чтобы создавать указатели на объекты в массиве, я просто объявлял все объекты по отдельности и создавал статический массив указателей на объекты.

Вроде так:

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

Кажется, это работает для меня. Я надеюсь, что это помогает.

0 голосов
/ 23 марта 2009
  typedef int (foo::*b_member_ptr)[2];
  b_member_ptr c= &foo::b;

все работает.

Небольшой трюк для использования указателей на элементы и функции.
попробуй написать

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

и в ошибке компилятора вы увидите ожидаемый тип, для вашего случая int (foo::*)[2].

EDIT
Я не уверен, что то, что вы хотите, является законным без этого указателя. Для добавления смещения 1 к вашему указателю вы должны получить указатель на массив из вашего указателя на массив элементов. Но вы можете разыменовать указатель на член без этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...