【问题标题】:Result of converting a pointer to function to different pointer to function type将指向函数的指针转换为指向函数类型的不同指针的结果
【发布时间】:2012-01-17 21:29:35
【问题描述】:

(5.2.10/6) C++03 指向函数的指针可以显式转换为指向不同类型函数的指针。调用函数的效果 通过指向函数类型 (8.3.5) 的指针 函数定义中使用的类型未定义。除了 将“指向 T1 的指针”类型的右值转换为“指针”类型 到 T2"(其中 T1 和 T2 是函数类型)并返回其原始 type 产生原始指针值,这种指针的结果 转换未指定。 [注:另见 4.10 了解更多细节 指针转换。]

以下是我正在尝试做的事情,虽然很明显将fp1 转换为fp2 的结果将产生一个原始指针,但同时标准中的措辞变为"The result of such a pointer conversion is unspecified" 什么是这个意思吗?

int f() { return 42; }

int main()
{
    void(*fp1)() = reinterpret_cast<void(*)()>(f);

    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);

    // Safe to call the function ?
    fp2();
}

【问题讨论】:

    标签: c++ function-pointers reinterpret-cast


    【解决方案1】:

    您误读了标准,“未指定”部分仅适用于其他转换:

    除了[特殊情况]这种指针转换的结果是未指定的。

    那个[特殊情况]是你转换回原始函数指针类型的那个,就像你的例子一样。在这种特殊情况下,转换会产生原始指针值,因此可以像在您的示例中一样使用它。

    仅对于其他转换,结果未指定。

    【讨论】:

    • 我猜其他的转换比如 ffp1 就我而言,对吧?
    • @user:是的,fp1 的内容是未指定的,但是如果你转换回原来的类型,你会得到原来的值。
    • 未定义的行为是如果您尝试执行(*fp1)();。这将调用一个返回int 的函数,就好像它正在返回void。至少,这将如何爆炸取决于您的架构的 ABI。如果它以旧 C 方式做事,在调用函数之前为返回的对象在堆栈上分配空间,那么在这种情况下,它不会为返回的 int 分配空间(因为它认为没有返回值),并且被调用的函数会将其返回值放在错误的位置(例如覆盖返回地址),事情就会从那里走下坡路。
    【解决方案2】:

    是的,它是安全的。
    reinterpret_cast 只允许您将一个指针转换为另一个指针,但它不保证任何安全性,并且使用此类指针的结果未指定,除非将其类型转换回其原始指针类型。

    提到的标准引用指定如果您将一种类型的函数指针类型转换为另一种类型并尝试通过它调用函数,则结果是未定义的。

    但是,
    reinterpret_cast 向您保证,如果您将类型转换后的指针转换回原始类型,则该指针的格式正确。

    您的代码尝试执行第二个,因此它是安全的。

    【讨论】:

      【解决方案3】:

      是的,你很安全。 “除了转换...”案例涵盖了您正在做的事情。

      之所以叫它,是为了让你通过另一种类型的函数指针来传递函数指针。所以你可以定义如下:

      enum CallbackType {
          eFuncPtrVoidReturningVoid,
          eFuncPtrVoidReturningInt,
          // ... more as needed ...
      };
      
      class CallbackRecord
      {
      public:
          CallbackRecord(void (*cb)()): cbType(eFuncPtrVoidReturningVoid), cbFunc(cb) 
              {}
          CallbackRecord(int (*cb)()): cbType(eFuncPtrVoidReturningInt), 
              cbFunc(reinterpret_cast<void (*)()>(cb)) {}
          void operator()() const;
      protected:
          CallbackType cbType;
          void (*cbFunc)();
      };
      
      void CallbackRecord::operator()() const
      {
          switch(cbType)
          {
          case eFuncPtrVoidReturningVoid:
              (*cbFunc)();
              break;
      
          case eFuncPtrVoidReturningInt:
              while((*reinterpret_cast<int (*)()>(cbFunc))())
                  ;
              break;
          }
      }
      

      虽然您可以只说“让所有回调返回 int”,但如果回调类型的数量超过两个,这将要求您为不符合调用约定的任何内容编写包装器。允许这些函数指针类型转换为您提供了一种支持多种回调类型的替代方法,并使我们无需将CallbackRecord 转换为模板。它还允许子类化或编组来替换上面的switch 语句,而无需使用virtual 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多