54

ご存じのとおり、C++11 ではconstexprキーワードが導入されました。

C++11 ではキーワード constexpr が導入されました。これにより、ユーザーは関数またはオブジェクト コンストラクターがコンパイル時の定数であることを保証できます。[...]これにより、コンパイラは[関数名]がコンパイル時の定数であることを理解し、検証できます。

私の質問は、宣言できる関数の形式になぜそのような厳しい制限があるのか​​ということです。関数が純粋であることを保証したいという欲求は理解していますが、次のことを考慮してください。

関数で constexpr を使用すると、その関数で実行できることがいくつか制限されます。まず、関数には void 以外の戻り値の型が必要です。次に、関数本体で変数を宣言したり、新しい型を定義したりできません。第 3 に、本文には、宣言、null ステートメント、および 1 つの return ステートメントのみを含めることができます。引数置換後に return ステートメントの式が定数式を生成するような引数値が存在する必要があります。

つまり、この純粋関数は不正です。

constexpr int maybeInCppC1Y(int a, int b)
{
    if (a>0)
        return a+b;
    else
        return a-b;
  //can be written as   return  (a>0) ? (a+b):(a-b); but that isnt the point
}

また、ローカル変数を定義することはできません... :(だから、これは設計上の決定なのか、それとも関数 a が純粋であることを証明することになると、コンパイラはうまくいかないのでしょうか?

4

5 に答える 5

29

式の代わりにステートメントを記述する必要がある理由は、ステートメントの追加機能、特にループ機能を利用したいからです。しかし、有用であるためには、変数を宣言する機能が必要です (これも禁止されています)。

可変変数を使用したループ機能と論理分岐 (ifステートメントなど) を組み合わせると、無限ループを作成できます。このようなループが終了するかどうかを判断することはできません (停止問題)。したがって、ソースによっては、コンパイラがハングアップすることがあります。

再帰的な純粋関数を使用すると、無限再帰を引き起こすことができます。これは、上記のループ機能と同等に強力であることを示すことができます。ただし、C++ はコンパイル時に既にその問題を抱えています。これはテンプレートの展開で発生します。そのため、コンパイラは「テンプレート スタックの深さ」のスイッチを既に持っている必要があり、いつあきらめるかがわかります。

したがって、この制限は、(C++ コンパイルが終了するかどうかを判断するという) この問題が、これまで以上に厄介なものにならないようにするために設計されているようです。

于 2011-11-29T10:20:45.920 に答える
29

関数のルールは、副作用のある関数を書くことができconstexprないように設計されています。constexpr

constexpr副作用がないことを要求することにより、ユーザーが実際にいつどこで評価されたかを判断できなくなります。constexpr関数はコンパイラの裁量でコンパイル時と実行時の両方で発生することが許可されているため、これは重要です。

副作用が許容される場合、それらが観察される順序についていくつかの規則が必要になります。それを定義するのは信じられないほど難しいでしょう -static初期化順序の問題よりもさらに難しいのです。

これらの関数に副作用がないことを保証するための比較的単純な一連のルールは、それらが単一の式であることを要求することです (さらに、いくつかの追加の制限があります)。これは最初は制限されているように聞こえ、指摘したように if ステートメントを除外します。その特定のケースには副作用はありませんが、ルールに余分な複雑さをもたらし、三項演算子または再帰を使用して同じことを記述できることを考えると、実際には大したことではありません。

n2235constexprは、 C++ での追加を提案した論文です。設計の合理性について説明します-関連する引用は、デストラクタに関する議論からのこれのようですが、一般的に関連しています:

その理由は、定数式は、組み込み型の他のリテラルと同様に、翻訳時にコンパイラによって評価されることを意図しているためです。特に、目に見える副作用は認められません。

興味深いことに、以前の提案ではコンパイラがconstexprnew キーワードのない関数を自動的に割り出すことが示唆されていましたが、これは実行できないほど複雑であることがわかりました。

(この論文で引用されている参考文献には他の引用があると思いますが、これは副作用がないという私の主張の重要なポイントをカバーしています)

于 2011-11-29T15:26:02.800 に答える
13

実際、C++ 標準化委員会は、c++14 でこれらの制約のいくつかを削除することを検討しています。次の作業文書を参照してください http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3597.html

于 2013-07-17T21:28:19.317 に答える
3

コンパイル時に実行できないコードや、常に停止することが証明されていないコードを有効にせずに、制限を大幅に解除することは確かに可能です。しかし、私はそれが行われなかったと思います

  • 最小限の利益のためにコンパイラを複雑にします。C++ コンパイラはそのままでは非常に複雑です

  • 上記の制限に違反せずに許可される量を正確に指定するには時間がかかり、標準を公開するために必要な機能が延期されていることを考えると、おそらくさらに作業を追加するインセンティブはほとんどありませんでした (そして、標準) わずかなゲイン

  • 制限のいくつかは、かなり恣意的またはかなり複雑でした (特にループでは、C++ にはネイティブのインクリメント for ループの概念がありませんが、終了条件とインクリメント コードの両方を明示的に指定する必要があります。 for ステートメント、それらに任意の式を使用できるようにする)

もちろん、私の仮定が正しいかどうか、信頼できる答えを出せるのは標準化委員会のメンバーだけです。

于 2011-11-29T19:24:52.757 に答える
-4

constexprはconstオブジェクト専用だと思います。つまり; コンストラクトのような静的constオブジェクトをString::empty_string静的に(ハッキングせずに!)持つことができるようになりました。これにより、「main」が呼び出されるまでの時間が短縮される場合があります。また、静的constオブジェクトには次のような関数が含まれている可能性がある.length(), operator==,...ため、「expr」が必要になります。'C'では、次のような静的定数構造体を作成できます。

static const Foos foo = { .a = 1, .b = 2, };

Linuxカーネルには、このタイプのクラスがたくさんあります。C ++では、constexprを使用してこれを実行できます。

注:わかりませんが、次のバージョンのように、以下のコードは受け入れられません。

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }
于 2013-02-01T08:37:02.460 に答える