web-dev-qa-db-ja.com

関数ポインタが `constexpr`になるのはなぜですか?

プログラムが実行される前に、コンパイラはメモリ内のどこに平方根があるかをどのように知るのですか?プログラムを実行するたびにアドレスが異なると思いましたが、これは機能します。

constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);

アドレスがメモリ内の別のアドレスに関連しているためですか? fpの値が大きいのでそうは思いません:0x720E1B94。

25
Coolwater

プログラムが実行される前に、コンパイラはメモリ内のどこに平方根があるかをどのように知るのですか?

ツールチェーンは、関数を配置する場所を決定します。

アドレスがメモリ内の別のアドレスに関連しているためですか?

生成されたプログラムが 再配置可能 または 位置に依存しない の場合、そうです。プログラムがどちらでもない場合、アドレスは絶対である可能性もあります。

次回プログラムを実行するときに、まったく同じメモリスポットが使用できるのはなぜですか?

メモリスペースが 仮想 であるため。

7
eerorika

コンパイル時に、コンパイラはsqrtのアドレスを知りません。ただし、コンパイル時に、そのポインターのaddressにアクセスできるようにするconstexpr関数ポインターでは何もできません。したがって、コンパイル時の関数ポインタは不透明な値として扱うことができます。

また、初期化後にconstexpr変数を変更することはできないため、すべてのconstexpr関数ポインターを特定の関数の場所にまとめることができます。

あなたがこのようなことをした場合:

_using fptr = float(*)(float);

constexpr fptr get_func(int x)
{
  return x == 3 ? &sqrtf : &sinf;
}

constexpr fptr ptr = get_func(12);
_

コンパイラは、特定のコンパイル時の値に対して_get_func_が返す関数を正確に検出できます。したがって、get_func(12)は_&sinf_になります。したがって、_&sinf_がコンパイルされるものはすべて、get_func(12)がコンパイルされるものとまったく同じです。

26
Nicol Bolas

アドレス値はリンカによって割り当てられるため、コンパイラは正確なアドレス値を知りません。

cout << fp(5.0); 

これは、正確なアドレスが解決された後の実行時に評価されるため、機能します。

一般に、constexprポインターの実際の値(アドレス)はコン​​パイル時にわからないため、使用できません。

BjarneStroustrupのC++プログラミング言語第4版言及:

10.4.5アドレス定数式

グローバル変数などの静的に割り当てられたオブジェクト(§6.4.2)のアドレスは定数です。ただし、その値はコンパイラではなくリンカによって割り当てられるため、コンパイラはそのようなアドレス定数の値を知ることができません。これにより、ポインタと参照型の定数式の範囲が制限されます。例えば:

   constexpr const char∗ p1 = "asdf";
   constexpr const char∗ p2 = p1;     // OK 
   constexpr const char∗ p2 = p1+2;   // error : the compiler does not know the value of p1 
   constexpr char c = p1[2];          // OK, c==’d’; the compiler knows the value pointed to by p1
17
SHH

簡単です。

コンパイラがこのコードで呼び出すアドレスをどのように知っているかを考えてみましょう。

puts("hey!");

コンパイラはputsの場所を認識しておらず、ランタイムルックアップも追加しません(実際にはクラスの仮想メソッドが実行する必要があるものですが、パフォーマンスにはかなり悪いでしょう)。 。実行時に異なるバージョンのダイナミックライブラリを使用する可能性(まったく同じライブラリファイルであっても、アドレス空間配置のランダム化は言うまでもありません)により、ビルド時のツールチェーンリンカーもそれを認識しなくなります。

したがって、コンパイルされたバイナリプログラムを起動するときに、アドレスを修正するのは ダイナミックリンカ 次第です。これは再配置と呼ばれます。

constexprでもまったく同じことが起こります。コンパイラは、このアドレスを使用してコード内のすべての場所を再配置テーブルに追加し、次にダイナミックリンカーを追加しますプログラムが開始するたびにその仕事をします。

5
hyde