web-dev-qa-db-ja.com

なぜC ++ 11 constexprはそれほど制限されているのですか?

おそらくご存じのとおり、C++ 11ではconstexprキーワードが導入されています。

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

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

関数でconstexprを使用すると、その関数が実行できる内容にいくつかの制限が課されます。まず、関数には非voidの戻り値の型が必要です。第2に、関数本体は変数を宣言したり、新しい型を定義したりできません。 3番目に、本文には宣言、nullステートメント、および単一の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が純粋であることを証明することになると、コンパイラは吸いますか?

53
NoSenseEtAl

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

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

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

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

28

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

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

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

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

n2235 は、C++でconstexprオプションを提案した論文です。それは設計の合理性について議論します-関連する引用はデストラクタに関する議論からのこれのようですが、一般的に関連しています:

その理由は、定数式は、組み込みタイプの他のリテラルと同様に、変換時にコンパイラーによって評価されることを意図しているためです。特に、観察可能な副作用は許可されていません。

興味深いことに、この論文では、以前の提案でコンパイラが新しいキーワードなしでどの関数がconstexprであるかを自動的に把握することを提案したが、これは機能が複雑であることがわかり、ルールが設計されたという私の提案をサポートしているようです簡単に。

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

28
Flexo

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

12
hivert

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

  • 最小限のゲインでコンパイラーを複雑にします。 C++コンパイラはそのままではかなり複雑です

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

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

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

3
celtschk