web-dev-qa-db-ja.com

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

非常に些細な関数を考えると、

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

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

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

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

私の質問はこれが悪い考えである状況はありますか?同様に、この関数をconstexprにすると、この関数が特定の状況、またはどこで誤動作しますか?

26
Xirema

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

そのため、constexpr修飾子は変更できない設計上の決定になります。 APIに互換性のない変更がない限り、この修飾子を削除することはできません。また、その機能を実装する方法も制限されます。この関数内でログを記録することはできません。すべての些細な機能が永遠に些細なままであるとは限りません。

つまり、本質的に純粋な関数である関数にはconstexprを使用する必要があり、コンパイル時に実際に役立ちます(たとえば、テンプレートのメタプログラミング)。現在の実装がたまたまconstexprに対応しているからといって、関数をconstexprにするのは良くありません。

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

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

19
amon

関数をconstexprとしてマークすると、インライン関数§[dcl.constexpr]/1にもなります。

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

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

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

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

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

constexpr関数もいくつかの方法で制限されているため、一部の関数では、それがまったくオプションにならない場合があります。制限は次のとおりです。

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

私はいくつかの曖昧なものをスキップしました(たとえば、gotoまたはasmステートメントを含めることもできません)が、かなり多くのことについて、機能しません。

結論:はい、これが適切ではない状況がかなりあります。

12
Jerry Coffin