なぜC++ 11のconstexprは制限的なのですか?



c++ 11 slide (4)

constexpr関数の規則は、 constexprがあるconstexpr関数を記述することは不可能なように設計されています。

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

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

これらの関数を副作用なしにするための比較的単純な規則は、それらが単なる式であることを要求することです(それに加えていくつかの制限があります)。 これは当初は制限されていたとみなし、あなたが指摘したようにif文を除外します その特殊なケースは副作用を伴わないが、ルールに複雑さを追加し、三項演算子を使って同じことを書くことができるか、再帰的にそれは本当に大きな問題ではないということを考えると、

n2235は、C ++でのconstexpr追加を提案した論文です。 関連する見積もりは、デストラクタに関する議論からのものですが、一般的には関連しています。

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

興味深いことに、以前の提案では、新しいキーワードなしでどの関数がconstexprあったかをコンパイラが自動的に把握することが示唆されていましたが、これはうまく動作しないほど複雑であることが判明しました。

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

ご存じのように、C ++ 11ではconstexprキーワードが導入されています。

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

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

ある関数にconstexprを使用すると、その関数が何をすることができるかにいくつかの制限が課せられます。 まず、関数はvoidでない戻り型を持たなければなりません。 第2に、関数本体は変数を宣言することも、新しい型を定義することもできません。 第3に、本文には宣言、ヌルステートメント、および単一の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
}

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



Answer #2

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

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

再帰的な純関数を使用することにより、無限再帰を引き起こすことが可能であり、これは上述のループ機能と同等に強力であることが示される。 しかし、C ++はすでにコンパイル時にこの問題を抱えています。これはテンプレートの拡張で起こります。そのため、コンパイラは「テンプレートスタックの深さ」のスイッチをすでに持っていなければなりません。

したがって、この問題(C ++のコンパイルが完了するかどうかを判断する)が既にそれよりも厄介なものにならないように、制限が設けられているようです。


Answer #3

コンパイル時に実行できないコードや、常に停止することが証明できないコードを有効にしなくても、この制限はかなり緩和されます。 しかし、私はそれが行われていないと思うので、

  • コンパイラの複雑さを最小限に抑えることができます。 C ++コンパイラはそのままでは複雑です

  • 上記の制限に違反せずにどれくらい許可されているかを正確に指定することは時間がかかり、標準をドアから出すために望ましい機能が延期されているため、おそらくもっと多くの作業を追加するインセンティブはほとんどありませんでした少しの利益のための基準)

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

もちろん、私の前提が正しいかどうかは、標準化委員会のメンバーだけが正式な回答を与えることができます。





constexpr