プログラムが実行される前に、コンパイラはメモリ内のどこに平方根があるかをどのように知るのですか?プログラムを実行するたびにアドレスが異なると思いましたが、これは機能します。
constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);
アドレスがメモリ内の別のアドレスに関連しているためですか? fp
の値が大きいのでそうは思いません:0x720E1B94。
コンパイル時に、コンパイラは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)
がコンパイルされるものとまったく同じです。
アドレス値はリンカによって割り当てられるため、コンパイラは正確なアドレス値を知りません。
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
簡単です。
コンパイラがこのコードで呼び出すアドレスをどのように知っているかを考えてみましょう。
puts("hey!");
コンパイラはputs
の場所を認識しておらず、ランタイムルックアップも追加しません(実際にはクラスの仮想メソッドが実行する必要があるものですが、パフォーマンスにはかなり悪いでしょう)。 。実行時に異なるバージョンのダイナミックライブラリを使用する可能性(まったく同じライブラリファイルであっても、アドレス空間配置のランダム化は言うまでもありません)により、ビルド時のツールチェーンリンカーもそれを認識しなくなります。
したがって、コンパイルされたバイナリプログラムを起動するときに、アドレスを修正するのは ダイナミックリンカ 次第です。これは再配置と呼ばれます。
constexpr
でもまったく同じことが起こります。コンパイラは、このアドレスを使用してコード内のすべての場所を再配置テーブルに追加し、次にダイナミックリンカーを追加しますプログラムが開始するたびにその仕事をします。