【问题标题】:Downcasting pointer to member function. Is this legal usage?向下转换指向成员函数的指针。这是合法使用吗?
【发布时间】:2019-06-10 18:05:56
【问题描述】:

我将指向成员函数的指针列表存储在一个数组中。我想索引到数组并执行适当的函数。将有许多数组列出来自不同类的函数(都从 Base 派生),因此在编译时该类是未知的。

我的方案有效,但我对不得不在一个地方使用 void 指针并不完全满意,但我似乎无法避免它。

根据 C++11 标准,我在 Base 和 Derived 成员函数指针之间的转换是否合法(它与 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);
}

【问题讨论】:

  • 请注意,函数指针、成员指针和成员函数指针不保证与void* 兼容。它们可能更大,并且转换为 void* 并不能保证返回原始值。将函数指针转换为对象指针是conditionally supported,并非所有实现都允许这样做。
  • 函数 f 正在接收一个指向 DerivedThunk::* 数组的指针(我认为它是一个普通的数据指针),放入一个 void* 中。然后它将 void* 重新解释为 BaseThunk* 以允许对其进行索引。我认为指向指针部分的数组是合法的,但隐含地假定 BaseThunk 和 DerivedThunk 的大小相同。不太确定这是否总是正确的。
  • 您可以在此处使用static_cast(这有助于避免混淆对象指针指向成员的指针)。不过,这并不能回答您的问题,因为正如您所说,void* 仍然参与其中。

标签: c++ inheritance language-lawyer member-function-pointers


【解决方案1】:

您对void* 的担忧是有根据的:这是未定义的行为,因为(在Intermediate::f 中)您正在对不匹配的指针执行指针运算并通读数组的类型。

好消息是有一个简单的解决方法:因为您的数组的目的是让派生类函数在仅给定 Base&amp;BaseThunk 的情况下被调用,您可以存储 那种类型:

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

static_casts 有点冗长,但完全合法,只要您使用生成的 BaseThunk 对象与类型为或派生自 Derived 的对象。您甚至不必先获得Derived*

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

【讨论】:

  • 谢谢。我没有想过在数组定义中使用 static_cast 。戴维斯鲱鱼似乎一直在朝着类似的方向思考。您的解决方案对我来说很好,因为无论如何数组都是机器生成的(问题与生成 C++ 源代码的编译器有关),而且我真的不在乎它是否冗长。 'execute' 被声明为虚拟的,因为在我的实际问题中它需要是虚拟的。我应该从我发布的玩具代码中删除它。
  • @user1759557:我想说 Davis Herring 的想法是非常类似的,尽管那只是关于reinterpret_cast。我很高兴这种方法对你有用,尽管为了打字/阅读方便,演员总是可以放在一个带有短名称的函数中。
  • 对不起,戴维斯,当我阅读您的评论时,我可能没有完全理解它。我以为你是说我可以用 static_cast 替换 'Intermediate' 中 void* 的 reinterpret_cast。现在似乎您指的是数组,但我第一次没有得到它。我使用#define 来缩短代码,但是您使用函数的想法要简洁得多,当然编译器会优化掉它。
  • @user1759557:不,你是对的——我首先评论了你使用的演员表,然后写了一个关于演员表的答案。
  • 哇!我没有注意到正确的答案来自同样评论的同一个人(你)。我试图对评论表示赞赏。
猜你喜欢
  • 1970-01-01
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-27
  • 2023-03-23
相关资源
最近更新 更多