非常に些細な関数を考えると、
int transform(int val) {
return (val + 7) / 8;
}
この関数をconstexpr
関数に変換するのは簡単で、constexpr
変数を定義するときに、次のように使用できるのは明らかです。
constexpr int transform(int val) {
return (val + 7) / 8;
}
私の想定では、これは厳密に改善された機能であり、関数はconstexpr
以外のコンテキストでも呼び出すことができ、コンパイル時の定数変数の定義にも使用できます。
私の質問はこれが悪い考えである状況はありますか?同様に、この関数をconstexpr
にすると、この関数が特定の状況、またはどこで誤動作しますか?
これは、関数がパブリックインターフェイスの一部であり、APIの将来のバージョンをバイナリ互換性を維持したい場合にのみ重要です。その場合は、APIをどのように進化させたいのか、将来の変更のために拡張ポイントが必要な場所を慎重に検討する必要があります。
そのため、constexpr
修飾子は変更できない設計上の決定になります。 APIに互換性のない変更がない限り、この修飾子を削除することはできません。また、その機能を実装する方法も制限されます。この関数内でログを記録することはできません。すべての些細な機能が永遠に些細なままであるとは限りません。
つまり、本質的に純粋な関数である関数にはconstexpr
を使用する必要があり、コンパイル時に実際に役立ちます(たとえば、テンプレートのメタプログラミング)。現在の実装がたまたまconstexprに対応しているからといって、関数をconstexprにするのは良くありません。
コンパイル時の評価が不要な場合は、インライン関数または内部リンケージ付きの関数を使用する方がconstexpr
よりも適切と思われます。これらすべてのバリアントには、関数本体が「パブリック」であり、呼び出し場所と同じコンパイル単位で使用できるという共通点があります。
問題の関数が安定したパブリックAPIの一部ではない場合、設計を自由に変更できるため、これは問題にはなりません。ただし、すべての呼び出しサイトを制御できるようになったため、関数constexprに「念のため」のマークを付ける必要はありません。あなたknowこの関数をconstexprコンテキストで使用しているかどうか。不必要に限定的な修飾子を追加すると、難読化と見なされる場合があります。
関数をconstexpr
としてマークすると、インライン関数§[dcl.constexpr]/1にもなります。
Constexpr指定子で宣言された関数または静的データメンバーは、暗黙的にインライン関数または変数です(7.1.6)。
inline
は、その関数の定義を、それが使用されるすべての翻訳単位に含める必要があることを意味します。つまり、constexpr
関数は次のいずれかでなければなりません。
ヘッダーで宣言してソースファイルで定義する最も一般的な関数(およびそれらを使用する他の関数は、ヘッダーのみを含み、そのソースのオブジェクトファイルへのリンク)constexpr
は機能しません。
理論的には、すべてをヘッダーに移動して、すべてのヘッダーを含むソースファイルを1つだけ持つことができると思いますが、これはコンパイル時間を大幅に低下させ、ほとんどの深刻なプロジェクトではコンパイルに大量のメモリが必要になります。
constexpr
関数もいくつかの方法で制限されているため、一部の関数では、それがまったくオプションにならない場合があります。制限は次のとおりです。
constexpr
になります。try
ブロックを含めることはできません。私はいくつかの曖昧なものをスキップしました(たとえば、goto
またはasm
ステートメントを含めることもできません)が、かなり多くのことについて、機能しません。
結論:はい、これが適切ではない状況がかなりあります。