Нисходящий указатель на функцию-член. Это законное использование? - PullRequest
4 голосов
/ 10 июня 2019

Я храню список указателей на функцию-член в массиве. Я хочу индексировать в массив и выполнить соответствующую функцию. Будет много массивов, перечисляющих функции из разных классов (все они получены из Base), поэтому класс не известен во время компиляции.

У меня есть работающая схема, но я не совсем доволен использованием указателя void в одном месте, но я не могу этого избежать.

Допустимо ли приведение между указателями на функции-члены Base и Derived согласно стандарту C ++ 11 (работает с g ++). Буду признателен за консультацию юриста по языку!

Ниже приведена урезанная, но работоспособная версия моего кода.

#include <iostream>

using std::cout;

//*************************************

class Base {
public:

  typedef int (Base::*BaseThunk)();

  virtual int execute(BaseThunk x) {return 0;}

};

//*************************************

class Derived : public Base {
public:

  typedef int (Derived::*DerivedThunk)();

  int execute(BaseThunk step) {
    return (this->*step)(); //Is this OK ? step is really a DerivedThunk.
  }

  int f1() { cout<<"1\n";return 1;}
  int f2() { cout<<"2\n";return 2;}
  int f3() { cout<<"3\n";return 3;}

  static DerivedThunk steps[];
};

//Here is an array of pointers to member functions of the Derived class.
Derived::DerivedThunk Derived::steps[] = {&Derived::f1, &Derived::f2, &Derived::f3};

//*************************************

class Intermediate : public Base {
public:

  void f(void *x) { //I am worried about using void pointer here !

    BaseThunk *seq = reinterpret_cast<BaseThunk *>(x);

    Derived d;
    d.execute(seq[2]);
  }

};

//*************************************

int main() {
  Intermediate b;

  b.f(&Derived::steps);
}

1 Ответ

1 голос
/ 11 июня 2019

Ваша обеспокоенность по поводу void* вполне обоснована: это неопределенное поведение , потому что (в Intermediate::f) вы выполняете арифметику указателя и читаете указатель, который не соответствует тип массива.

Хорошая новость заключается в том, что есть простое решение: поскольку ваши массивы должны вызывать функции производного класса, задавая только Base& и BaseThunk, вы можете store этот тип:

Base::BaseThunk Derived::steps[]=
  {static_cast<BaseThunk>(&Derived::f1),
   …};

static_cast несколько многословны, но совершенно законны, если вы используете результирующих BaseThunk объектов с объектом, тип которого является производным от Derived. Вам даже не нужно сначала получать Derived*:

int Base::execute(BaseThunk x)  // no need to be virtual
{return (this->*x)();}
...