7

関数ポインタをvoid*変数にキャストすることを再解釈したいと思います。関数ポインタのタイプはタイプになりますClass* (*)(void*)

以下はサンプルコードです。

class Test
{
    int a;
};

int main()
{
    Test* *p(void **a);
    void *f=reinterpret_cast<void*>(p);     
}

上記のコードは、Visual Studio/x86コンパイラでうまく機能します。しかし、ARMコンパイラでは、コンパイルエラーが発生します。理由はわかりません。

エラー:#694:reinterpret_castはconstまたは他の型修飾子をキャストできません

関数ポインタを別の型にキャストするの説明を読みました

以下の説明が気になりました。

関数ポインタと通常のポインタの間のキャスト(たとえば、avoid (*)(void)をaにキャストするvoid*)。一部のアーキテクチャでは、追加のコンテキスト情報が含まれている可能性があるため、関数ポインタは必ずしも通常のポインタと同じサイズである必要はありません。これはおそらくx86で問題なく動作しますが、未定義の動作であることを忘れないでください。

void (*)(void*) -> void*少なくともほとんどのコンパイラでほぼ同じようにコンパイルされるように、このような変換を効果的に行うにはどうすればよいですか?

4

5 に答える 5

8

reinterpret_cast関数へのポインタをにキャストするために使用することはできませんvoid*。static、reinterpret、constキャストの組み合わせでは許可されない、Cキャストで実行できる追加の機能がいくつかありますが、その変換はそれらの1つではありません。

Cではキャストは許可されていますが、その動作は定義されていません(つまり、往復でさえも機能することが保証されていません)。

一部のPOSIX関数は、変換が十分に役立つようにする必要があります。

私はここにいるいくつかのコンパイラで遊んだことがあります:

  • 最高の適合モードであっても、Cキャストを妨げるものはありません。警告と適合レベルに応じて警告を出すものもあれば、私が試しても警告を出さないものもあります。
  • reinterpret_castは、よりリラックスしたレベルでも一部のコンパイラではエラーでしたが、他のコンパイラは警告を出さずにすべての場合にそれを受け入れました。

C ++ 0Xで利用可能な最後のドラフトではreinterpret_cast、関数ポインターとオブジェクトポインターの間が条件付きでサポートされています。

それが理にかなっているかどうかは、コンパイラよりもターゲットに依存することに注意してください。gccのようなポータブルコンパイラは、ターゲットアーキテクチャと場合によってはABIによって課せられる動作をします。

他の人が発言しているように、

Test* *p(void **a);

関数へのポインタではなく、関数を定義します。ただし、関数から関数へのポインタへの暗黙の変換は、reinterpret_castの引数に対して行われるため、reinterpret_castが取得するのはTest** (*p)(void** a)です。

問題をより深く再考させてくれたRichardのおかげで(記録のために、オブジェクトへのポインターへの関数へのポインターは、CキャストがC ++キャストの組み合わせによって許可されていないものを許可した1つのケースであると誤解しました)。

于 2009-08-20T08:39:51.117 に答える
7

reinterpret_castは、次の目的でのみ使用できます。

  • 恒常性を追加
  • ポインタを保持するのに十分な大きさの整数型に変換し、元に戻す
  • 関数へのポインタを別のタイプの関数へのポインタに変換します
  • オブジェクトへのポインタを別のタイプのオブジェクトへのポインタに変換します
  • メンバー関数へのポインターを異なるタイプのメンバー関数へのポインターに変換します
  • メンバーオブジェクトへのポインターを異なるタイプのメンバーオブジェクトへのポインターに変換します
  • 上記のルールを使用して2番目のキャストが可能な場合は常に、(組み込みおよびを使用して)とreinterpret_cast<T&>(x)同等です。*reinterpret_cast<T*>(&x)&*

(規格のセクション5.2.10を参照)

これは特に、ポインタから関数へのキャストvoid *は不可能ですが、へのキャストは可能であることを意味しvoid(*)()ます。


編集(2017):上記の答えはC++03に対してのみ正しいです。C++11からC++17では、関数ポインター間の変換void *が許可されているかどうかが実装で定義されています。これは通常、POSIX互換システムの場合に当てはまります。これは、dlsym()が返されるように宣言されてvoid *おり、クライアントはreinterpret_cast正しい関数ポインター型を返すことが期待されているためです。

許可される変換の完全なリストについては、cppreference.comを参照してください。

于 2009-08-20T10:40:15.803 に答える
4

さまざまなタイプの関数ポインタをリストに格納するだけの場合は、一般的な関数ポインタタイプにキャストできます。

class Test {
  int a;
};

int main()
{
  Test* *p(void **a);
  void (*f)()=reinterpret_cast<void (*)()>(p);
}

reinterpret_castこれは、 (5.2.10 / 6)を介して行うのに有効です。

関数へのポインターは、別のタイプの関数へのポインターに明示的に変換できます。関数の定義で使用されている型と同じではない関数型(8.3.5)へのポインターを介して関数を呼び出す効果は未定義です。タイプ「pointertoT1」の右辺値をタイプ「pointertoT2」(T1とT2は関数型)に変換し、元のタイプに戻すことを除いて、元のポインター値が生成されます。このようなポインター変換の結果は指定されていません。 。

于 2009-08-20T09:46:39.297 に答える
3

他の人はあなたがそのキャストを行うことができないと指摘しました(強く言えば、をvoid*使用して何かにキャストすることreinterpret_castも許可されていません-しかしコンパイラによって黙って許容されstatic_castます。ここで使用されることを意図しています)。

私は通常、型のパンニングを行う次のことを行います。これは、のマンページによると、推奨される方法です(これは、逆の操作、つまり関数ポインターへのキャストdlopenに関するものです) 。関数ポインタのアドレスを取得すると、データポインタが得られます。関数ポインタへのポインタ。これにより、にキャストできます。(関数ポインタではなく)を指しているふりをして、それを読み取ります。 void* void*void*

Test* (*pF)(void **a);
void *p = *(void**)(void*)&pF;

中間キャストは、内部でvoid*2つを使用するのと同等にしstatic_cast、型のパンニングに関する警告についてGCCを静かにします。static_castC ++スタイルのキャストを使用すると、これは2つのの組み合わせのように見えます

void *p = *static_cast<void**>(static_cast<void*>(&pF));

この手法を使用すると、GCCは左右のタイプのサイズが異なると自動的に認識し、その場合は警告を発することがわかりました。言うまでもなく、この制限を回避しようとするすべての手法と同様に、これは未定義の動作です。


関数があり、void*それをポイントしたい場合は、すべてを1行で実行できますが、構文が少し面倒です。これがどのように行われるかですが、読むのに問題がある場合はお勧めしません-ただし、マクロ内で使用することはできます

// using the function declaration you provided
Test** pF(void **a); 
void *p = *(void**)(void *) &(Test** (* const&)(void **a))&pF;

型を実行できるようにするための秘訣は、一時関数ポインターをconstへの左辺値参照に変換することです。これは、アドレスを取得して、上記のように進めることができます。

明示的なC++スタイルのstatic_castキャストを使用すると、定数を考慮に入れる必要があるため、これははるかに複雑に見えます。Cスタイルのキャストは自動的にそれを処理しました。楽しむ!

int main() {
    Test** pF(void **a);
    void *p = *static_cast<void* const*>(
      static_cast<void const*>(
        &static_cast<Test** (* const&)(void **a)>(&pF)));
}
于 2009-08-20T13:17:52.917 に答える
0

すべきではTest* *p(void **a);ないTest* (*p)(void **a)

多分それはあなたの問題ですか?

于 2009-08-20T09:17:39.277 に答える