C ++関数constexprをマークするのは悪いことですか?


26

非常に簡単な関数を考えると、

int transform(int val) {
    return (val + 7) / 8;
}

この関数を関数に変換するconstexprことは簡単で、constexpr変数を定義するときに次のように使用できることは非常に明白です。

constexpr int transform(int val) {
    return (val + 7) / 8;
}

私の想定では、これは厳密に改善されたものです。関数はconstexprコンテキスト以外でも呼び出すことができ、コンパイル時の定数変数の定義に使用できるようになったためです。

私の質問は、これが悪い考えである状況はありますか?たとえば、この関数を作成することによりconstexpr、特定の状況でこの関数が使用できなくなる状況や、誤動作する状況に遭遇することはありますか?


1
私が考えることができた唯一のものはコンパイラのバグでした。再帰的なconstexpr関数呼び出しにより、コンパイルステップが非常に遅くなったり、コンパイラーがメモリ不足でクラッシュしたりする可能性があります。
ザンリンクス

回答:


19

これは、関数がパブリックインターフェイスの一部であり、APIの将来のバージョンをバイナリ互換に保ちたい場合にのみ重要です。その場合、APIをどのように進化させたいか、そして将来の変更のために拡張ポイントが必要な場所を慎重に検討する必要があります。

これにより、constexpr修飾子は取り消せない設計上の決定になります。APIに互換性のない変更を加えない限り、この修飾子を削除することはできません。また、その関数を実装する方法も制限します。たとえば、この関数内でロギングを実行することはできません。すべての些細な機能が永遠に些細なものになるわけではありません。

あなたは、好ましくは、使用する必要があることを意味しconstexprている機能のために本質的に(テンプレートメタプログラミング用など)の純粋な機能、そしてそれはコンパイル時に実際に有用であろう。現在の実装がたまたまconstexprに対応しているという理由だけで、関数をconstexprにするのは良くありません。

コンパイル時の評価が不要な場合、インライン関数または内部リンケージのある関数を使用する方が適切ですconstexpr。これらのすべてのバリアントには、関数本体が「パブリック」であり、呼び出し場所と同じコンパイル単位で使用できるという共通点があります。

問題の関数が安定したパブリックAPIの一部ではない場合、設計を任意に自由に変更できるため、これは問題ではありません。ただし、すべての呼び出しサイトを制御できるようになったため、関数constexprを「念のため」にマークする必要はありません。あなたは知っているあなたがconstexprのコンテキストでこの機能を使用しているかどうか。その後、不必要に制限的な修飾子を追加すると、難読化と見なされる場合があります。


12

関数にマークをconstexpr付けると、インライン関数にもな​​ります§[dcl.constexpr] / 1:

constexpr指定子で宣言された関数または静的データメンバーは、暗黙的にインライン関数または変数です(7.1.6)。

inline、順番に、あなたはそれが使用される可能性のあるすべての翻訳単位にその関数の定義を含める必要があることを意味します。つまり、constexpr機能は次のいずれかでなければなりません。

  1. 1つの翻訳単位での使用に制限されている、または
  2. ヘッダーで定義されます。

ヘッダーで宣言し、ソースファイルで定義する最も一般的な関数(およびそれらを使用するものはすべてヘッダーのみを含み、そのソースのオブジェクトファイルに対するリンク)は、constexpr単に機能しません。

理論的には、すべてをヘッダーに移動し、すべてのヘッダーを含むソースファイルを1つだけ持つことができると思いますが、これによりコンパイル時間が大幅に遅くなり、ほとんどの深刻なプロジェクトではコンパイルに膨大な量のメモリが必要になります。

constexprこの関数はまた、いくつかの点で制限されているので、いくつかの機能のために、それはすべての選択肢ではないかもしれません。制限事項は次のとおりです。

  1. 仮想関数はできませconstexpr
  2. 戻り値の型は「リテラル型」でなければなりません(たとえば、非trival ctorまたはdtorを持つオブジェクトはありません)。
  3. パラメーターはすべてリテラル型である必要があります。
  4. 関数本体にtryブロックを含めることはできません。
  5. 非リテラル型の変数定義、または静的またはスレッドの保存期間を持つものを含めることはできません。

あいまいなことをいくつかスキップしました(たとえば、a gotoasmstatementを含めることもできません)が、アイデアは得られます。

一番下の行:はい、これが悪いアイデアになるかなりの状況があります。


「仮想であってはなりません(C ++ 20まで)」仮想関数をconstexprにするにはどうすればよいのでしょうか。コンパイラは何をしますか?
chaosink
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.