web-dev-qa-db-ja.com

constexprはnoexceptを意味しますか?

constexpr指定子は関数のnoexcept指定子を意味しますか? Answer to 同様の質問inline指定子に関して「はい」と言いますが、 Eric Nieblerの記事 現在のもの。私の考えでは、答えはconstexpr関数を使用するコンテキストに依存する可能性があります。それは定数式コンテキストか実行時コンテキストか、つまりコンパイル時に既知の関数のすべてのパラメーターかどうかです。

答えは「はい」だと思っていましたが、 簡単なチェック はそうではないことを示しています。

constexpr
bool f(int) noexcept
{
    return true;
}

constexpr
bool g(int)
{
    return true;
}

static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour

#include <cassert>
#include <cstdlib>

int
main(int argc, char * [])
{
    assert(noexcept(f(argc)));
    assert(noexcept(g(argc)));
    return EXIT_SUCCESS;
}
36

Constexpr関数のすべての呼び出しをコア定数式の部分式として評価できる必要があるわけではないため、これは当てはまりません。これを可能にする引数値は1つだけ必要です。したがって、constexpr関数には、そのブランチを呼び出さない引数値がある限り、throwステートメントを含めることができます。

これはドラフトC++ 14標準セクションでカバーされています7.1.5 constexpr関数で何が許可されているかを示すconstexpr指定子[dcl.constexpr]:

Constexpr関数の定義は、次の制約を満たす必要があります。

  • 仮想であってはならない(10.3)。

  • その戻り値の型はリテラル型でなければなりません。

  • そのパラメータタイプのそれぞれは、リテラルタイプでなければなりません。

  • その関数本体は、=削除、=デフォルト、またはを含まない複合ステートメントでなければなりません。

    • asm定義、

    • gotoステートメント、

    • トライブロック、または

    • 非リテラルタイプ、静的またはスレッドストレージ期間の変数、または初期化が実行されない変数の定義。

ご覧のとおり、これはthrowを禁止していません。実際、 constexpr関数の制約を緩和する 提案がC++ 14の一部になったため、ほとんど禁止されていません。

以下に、constexpr関数は、コア定数式の部分式として評価できるように、少なくとも1つの引数値が存在する場合に整形式であるという規則を示します。

非テンプレート、非デフォルトのconstexpr関数、または非テンプレート、非デフォルト、非継承のconstexprコンストラクターの場合、関数またはコンストラクターの呼び出しなどの引数値が存在しない場合コア定数式(5.19)の評価された部分式である可能性があり、プログラムの形式が正しくありません;診断は必要ありません。

この段落の下に次の例があります。これは、この場合の完璧な例を示しています。

constexpr int f(bool b)
  { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required

したがって、次の例の出力が期待されます。

#include <iostream>

constexpr int f(bool b)   { return b ? throw 0 : 0; } 

int main() {
    std::cout << noexcept( f(1) ) << "\n" 
              << noexcept( f(0) ) << "\n" ; 
}

to be(gccと一緒にライブを参照):

 0
 1

webcompiler 経由のVisualStudioでも同じ結果が得られます。 hvdが指摘したように、clangにはバグレポートに記載されているバグがあります 式が定数式であるかどうかを確認する必要があります

欠陥レポート1129

欠陥レポート1129:constexpr関数のデフォルトのnothrow 同じ質問をします:

Constexpr関数は、例外を介して戻ることは許可されていません。これは認識されるべきであり、明示的な例外指定なしでconstexprとして宣言された関数は、通常のnoexcept(false)ではなくnoexcept(true)として宣言されたものとして扱われる必要があります。明示的な例外指定なしでconstexprと宣言された関数テンプレートの場合、constexprキーワードが特定のインスタンス化で尊重される場合に限り、noexcept(true)と見なされます。

応答は次のとおりです。

前提が正しくありません。定数式を必要とするコンテキストでconstexpr関数が呼び出された場合にのみ、例外が禁止されます。通常の機能として使用し、投げることができます。

そしてそれは5.3.7 [expr.unary.noexcept]パラグラフ3の箇条書き1を修正しました(強調して記された追加):

非スロー例外仕様(15.4 [except.spec])を持たない関数、メンバー関数、関数ポインター、またはメンバー関数ポインターへの潜在的に評価されたcall80、呼び出しがない限りは定数式(5.20 [expr.const])、

26
Shafik Yaghmour

言われていますnoexceptの:

式に、定数式でない限り、スローしない例外仕様を持たない任意のタイプの関数への呼び出しが含まれている場合、結果はfalseになります。

また、constexprについて、 それは本当です それ:

noexcept演算子は、定数式に対して常にtrueを返します

誰かが反例でコメントに示し、あなたも検証したように、いかなる状況においても、constexpr指定子が囲まれた式にnoexcept指定子を強制することを意味するようには見えません。

とにかく、ドキュメントから、noexceptconstexprの関係について次の興味深いメモがあります。

Noexcept演算子は定数式に対して常にtrueを返すため、constexpr関数の特定の呼び出しが定数式ブランチを取得するかどうかを確認するために使用できます。

編集:GCCの例

私の最後の引用についてのGCCでの彼の興味深いコメント/例について@hvdに感謝します。

_constexpr int f(int i) {
    return i == 0 ? i : f(i - 1);
}

int main() {
    noexcept(f(512));
    return noexcept(f(0)) == noexcept(f(0));
}
_

上記のコードは_0_を返し、ステートメントnoexcept(f(512))は効果がないことを警告します。
効果がないと思われるステートメントをコメントアウトすると、戻り値は_1_に変わります。

編集:clangの既知のバグ

繰り返しになりますが、@ hvdにも this リンクがあります。これは、質問で言及されているコードに関するclangのよく知られたバグに関するものです。

バグレポートからの引用:

C++の本、[expr.unary.noexcept] p3を引用してください:

「評価される可能性のあるコンテキストで、スローされない例外仕様を持たない関数、メンバー関数、関数ポインター、またはメンバー関数ポインターへの評価される可能性のある呼び出しが式に含まれる場合、noexcept演算子の結果はfalseになります。 (15.4)、呼び出しが定数式(5.19) "でない限り。

その最後のフレーズは実装していません。

5
skypjack

Constexpr関数で例外をスローすることが許可されています。これは、実装者がコンパイラにエラーを示すことができるように設計されています。次の機能があると考えてください。

constexpr int fast_sqrt(int x) {
    return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}

この場合、xが負の場合、コンパイルをすぐに停止し、コンパイラエラーを介してユーザーに問題を示す必要があります。これは、コンパイラエラーがランタイムエラーよりも優れている(失敗が早い)という考えに従います。

C++標準は(5.20)でこれを述べています:

条件式eは、抽象マシン(1.9)の規則に従ってeを評価すると、次の式のいずれかが評価されない限り、コア定数式です。

—スロー式(5.17)

3
Andrew

いいえ、一般的にはそうではありません。

Constexpr関数は、例外をスローできる非consteprコンテキストで呼び出すことができます(もちろん、手動でnoexcept(true)に指定した場合を除く)。

ただし、定数式(例のように)の一部として、noexcept(true)として指定されているかのように動作する必要があります(もちろん、式の評価によって例外がスローされる場合は、次のことができます。プログラムがまだ実行されていないため、std::terminateが呼び出されることはありませんが、代わりにコンパイル時エラーが発生します)。

私が予想したように、あなたの例はMSVCとg ++で静的アサーションをトリガーしません。これがclangのバグなのか、標準の何かを誤解しているのかはわかりません。

2
MikeMB