関数へのポインタを新しく割り当てることは合法ですか?


33

関数へのポインターはvoid *ポインターに格納できないため、単純なデータポインターではありません。それにもかかわらず、以下のコードのように、関数ポインターのコピーを動的メモリ(gccとclang内)に保存できるようです。そのようなコードはC ++標準に従って合法ですか、またはこれはある種のコンパイラ拡張ですか?

さらに、結果の関数ポインターへのポインターはプレーンデータポインターとして動作します。私はそれをvoid *に格納し、static_castによってvoid *から取得できます。この動作は規格によって保証されていますか?

int main()
{
  extern void fcn();
  void (*fcnPtr)() = &fcn;
  void (**ptrToFcnPtr)() = nullptr;

  //Make the copy of fcnPtr on the heap:
  ptrToFcnPtr = new decltype(fcnPtr)(fcnPtr);
  //Call the pointed-to function : 
  (**ptrToFcnPtr)();

  //Save the pointer in void* :
  void *ptr = ptrToFcnPtr;
  //retrieve the original ptr: 
  auto myPtr = static_cast< void(**)() > (ptr) ; 
  //free memory:
  delete ptrToFcnPtr ;

}

2
生の関数ポインタは使用しないでください。std::function代わりに使用してください。
プログラマー

newにキャストする必要はありませんvoid*。は関数ではなくオブジェクトなvoid* ptr = &fcnPtr;ので、同様にfcnPtr機能します。
ウォールナット

5
@Someprogrammerdude std::functionは、任意の呼び出し可能オブジェクトを格納するための型消去されたコンテナーであり、実際には関数ポインターの代わりではありません…
Michael Kenzel

7
(@Someprogrammerdude)むやみに使用/推奨しないでくださいstd::function。「ポリモーフィック」関数(つまり、一部のラムダの場合のように状態が含まれていても、適切なシグネチャを持つもの)を格納する機能は優れていますが、不要なオーバーヘッドも追加されます。関数へのポインタはPODです。A std::functionはありません。
マシュー

2
@マシュー公平に言うと、エイドリアンは関数へのポインターを動的に割り当て、型消去void*でそれを指し示すことを求めているので、この質問の文脈ではstd::functionまさに彼らが探していたもののようです。SPDによる関数ポインタの一般的な却下は不健全であることに同意します。
eerorika

回答:


27

関数ポインターはオブジェクトポインターではありませんが、「ある型の関数へのポインター」は、オブジェクト型[basic.types] / 8のままです。したがって、関数ポインタはそれ自体がオブジェクトであり、それらが指すものだけではありません。

したがって、新しい式を使用して関数ポインタ型のオブジェクトを作成できます…


9

それら(関数ポインタ)はvoid *ポインタに格納できないためです。

実際には、関数ポインタをとして保存することvoid*は条件付きでサポートされています。つまり、言語の実装に応じて、保存できるかどうかが決まります。言語の実装が動的読み込みをサポートしている場合は、void*おそらく関数ポインタの変換がサポートされています。GCC、Clang、MSVCはすべてこれをサポートしています。

reinterpret_cast<void*>(&function);

関数へのポインタを新しく割り当てることは合法ですか?

承知しました。関数ポインターを含むすべてのポインターはオブジェクトであり、すべてのオブジェクトを動的に割り当てることができます。

さらに、結果として得られる関数ポインターへのポインターは、プレーンデータポインターとして動作します。

関数ポインタはオブジェクトです。関数ポインタだけではなく、「振る舞いとして」へのポインタが、あるオブジェクトへのポインタ。

これをvoid *に格納し、static_castによってvoid *から取得できます。この動作は規格によって保証されていますか?

voidへのポインターとオブジェクトへのポインターの間の変換は可能です。そして、往復変換は元のポインタを生成することが保証されています。


ありがとう。しかし、関数ポインタをvoid *(またはその逆)に変換するには、reinterpret_castを使用する必要がありますよね?
エイドリアン

1
@エイドリアンはい。例を追加しました。
eerorika

誰かがおそらく例外を望んでいる場合:dos中規模メモリモデル+オーバーレイ。関数ポインターは中規模モデルのデータポインターよりも大きく、十分に努力すればオーバーレイを使用してプラグインを実現できます。
ジョシュア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.