54

C++11 では、constexpr指定子を使用して宣言された関数を、テンプレート引数などの定数式で使用できます。何が許可されるかについては、厳しい要件がありますconstexpr。基本的に、そのような関数は 1 つの部分式だけをカプセル化し、他には何もカプセル化しません。(編集: これは C++14 では緩和されていますが、疑問は残ります。)

そもそもなぜキーワードが必要なのですか? 得られるものは何ですか?

インターフェイスの意図を明らかにするのに役立ちますが、関数が定数式で使用できることを保証することにより、その意図を検証しません。関数を記述した後constexpr、プログラマーは次のことを行う必要があります。

  1. テスト ケースを作成するか、実際に定数式で使用されることを確認します。
  2. 定数式のコンテキストで有効なパラメーター値を文書化します。

意図を明らかにするのとは反対に、関数を で装飾するとconstexpr、中心的な意味上の制約を無視して接線の構文上の制約がチェックされるため、誤った安心感が追加される可能性があります。


要するにconstexpr、関数宣言が単なるオプションである場合、言語に望ましくない影響がありますか? それとも、有効なプログラムに何らかの影響がありますか?

4

4 に答える 4

44

クライアントコードがあなたが約束している以上のものを期待するのを防ぐ

ライブラリを作成していて、現在定数を返す関数がそこにあるとします。

awesome_lib.hpp

inline int f() { return 4; }

必要がない場合constexprは、クライアントコードの作成者であるあなたが立ち去って次のようなことをする可能性があります。

client_app.cpp

#include <awesome_lib.hpp>
#include <array>

std::array<int, f()> my_array;   // needs CT template arg
int my_c_array[f()];             // needs CT array dimension

次にf()、構成ファイルから値を返すように変更すると、クライアントコードが壊れますが、コードが壊れてしまうリスクがあるとは思いません。実際、本番環境に問題があり、再コンパイルに行く場合にのみ、この追加の問題が再構築を妨げることに気付く可能性があります。

の実装のみを変更することで、インターフェースf()の使用法を効果的に変更できたはずです。

代わりに、C ++ 11以降が提供するconstexprので、クライアントコードは、関数がaのままであるという合理的な期待を持ち、constexprそれをそのまま使用できることを示すことができます。 私は自分のインターフェースの一部としてそのような使用法を認識し、承認しています。 C ++ 03の場合と同様に、コンパイラは、constexpr上記の「不要な/不明な依存関係」シナリオを防ぐために、クライアントコードが他の非関数に依存するように構築されていないことを保証し続けます。これはドキュメント以上のものです-コンパイル時の強制です。

これは、プリプロセッサマクロの従来の使用法のより良い代替手段を提供するというC ++の傾向を継続していることは注目に値します(#define F 4クライアントプログラマーが、libプログラマーが言うことを変えるのが公正なゲームであると見なすかどうかをどのように知るかを検討してください#define F config["f"])。言語の名前空間/クラススコープシステムの外にあるものとして。

「明らかに」決して定数ではない関数の診断がないのはなぜですか?

constexprここでの混乱は、結果が実際にコンパイル時の定数である引数のセットがあることを積極的に保証していないためだと思います。むしろ、プログラマーがその責任を負う必要があります(そうでない場合は標準の§7.1.5/5プログラムの形式が正しくないと見なされますが、コンパイラーが診断を発行する必要はありません)。はい、それは残念ですが、上記のユーティリティを削除するものではありませんconstexpr

したがって、「実際にはconst値を返すことができない関数をコンパイルできるのはなぜですか」という質問から、 何が重要なのconstexprか」という質問に切り替えると役立つかもしれません。。constexpr

回答:任意の数の組み合わせを含む可能性のある徹底的なブランチ分析が必要になるためです。診断するのは、コンパイル時間やメモリ(想像できるハードウェアの能力を超えていても)で非常にコストがかかる可能性があります。さらに、そのようなケースを正確に診断しなければならないことが実際的である場合でも、コンパイラの作成者(彼らの時間のより良い使用法を持っている)のためのまったく新しいワームの缶です。constexprまた、検証が実行されたときに表示される必要がある関数内から呼び出された関数(および関数が呼び出す関数など)の定義など、プログラムにも影響があります。

一方、const値としての使用はconstexpr引き続き禁止されており、厳格さは重要ではありませんconstexpr。上に示したように、これは便利です。

非`const`メンバー関数との比較

  • constexpr防止int x[f()]しますが、const防止しませんconst X x; x.f();-どちらも、クライアントコードが不要な依存関係をハードコーディングしないようにします

  • どちらの場合も、コンパイラが-nessを自動的に決定することは望ましくありませんconst[expr]

    • const関数が進化して観測可能な値を変更し、クライアントコードを壊すことがすでに予想できる場合は、クライアントコードがオブジェクトのメンバー関数を呼び出さないようにする必要があります。

    • 後で実行時に決定されることがすでに予想されている場合は、値をテンプレートパラメータまたは配列ディメンションとして使用する必要はありません。

  • それらは、コンパイラーがメンバー関数内で他のメンバーの使用を強制するという点で異なりますが、コンパイル時定数の結果を強制しません(実際のコンパイラーの制限のため)constconstconstexpr

于 2013-01-23T04:39:26.953 に答える
3

キーワードがないと、コンパイラは間違いを診断できません。コンパイラーは、関数が構文的に無効であることを。として通知することはできませんconstexpr。これは「誤った安心感」をもたらすとおっしゃっていましたが、これらのエラーはできるだけ早く発見したほうがいいと思います。

于 2013-01-23T04:29:57.130 に答える
3

なしでも生活できますconstexprが、場合によっては、コードがより簡単で直感的になります。
たとえば、ある参照長を持つ配列を宣言するクラスがあります。

template<typename T, size_t SIZE>
struct MyArray
{
  T a[SIZE];
};

通常、次のように宣言できますMyArray

int a1[100];
MyArray<decltype(*a1), sizeof(a1)/sizeof(decltype(a1[0]))> obj;

それがどのようになるか見てみましょうconstexpr

template<typename T, size_t SIZE>
constexpr
size_t getSize (const T (&a)[SIZE]) { return SIZE; }

int a1[100];
MyArray<decltype(*a1), getSize(a1)> obj;

つまり、任意の関数 ( などgetSize(a1)) は、コンパイラが として認識する場合にのみ、テンプレート引数として使用できますconstexpr

constexprまた、負論理のチェックにも使用されます。特定のオブジェクトがコンパイル時にあることを保証します。ここに参照リンクがあります。

int i = 5;
const int j = i; // ok, but `j` is not at compile time
constexprt int k = i; // error
于 2013-01-23T04:36:11.900 に答える