Элемент-указатель на элемент массива

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

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

Ответ 1

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

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

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

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.

Ответ 3

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

Ответ 4

  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].

ИЗМЕНИТЬ
Я не уверен, что то, что вы хотите, является законным без этого указателя. Чтобы добавить 1 смещение к вашему указателю, вы должны получить указатель на массив из вашего указателя на массив элементов. Но вы можете разделить указатель на элемент без этого.

Ответ 5

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

Так же:

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

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