web-dev-qa-db-ja.com

C ++でnoexceptを使用する方法またはそれはどのように機能しますか?

C++ 11/14のキーワードでのnoexceptの使用法と目的を理解できません。つまり、exceptionsを発行しない関数のシグネチャです。しかし、それは本当に機能しますか?

以下のこのコードを見てください:

_#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void seev (vector<int> &v) noexcept;
void seev (vector<int> &v) noexcept
{
    for (int i=0;i<10;++i)
    {
        cout<<v.at(i)<<' ';
    }
}
int main()
{
    vector<int> v {1,2,3,4,5};
    seev(v);
    return 0;
}
_

上記のコードは確かに_out_of_range exception_をスローします。それで、ここでのnoexceptの使用は役に立たないのですか、それともそうですか?

私の質問は:

  1. noexceptはどのように機能しますか?

  2. どのように使用されますか?

  3. throw()noexceptで実行できなかったことは何ですか?

17
Ankit Acharya

関数のnoexcept仕様は、関数が例外をスローする必要があるかどうかをプログラマーがコンパイラーに通知するための単なる方法です。

コンパイラーはこの情報を使用して、非スロー関数の特定の最適化を有効にし、noexcept演算子を有効にします。これにより、コンパイル時に、特定の式が例外をスローするように宣言されているかどうかを確認できます。

たとえば、std :: vectorなどのコンテナは、要素のmoveコンストラクターがnoexceptの場合は要素を移動し、それ以外の場合はコピーします(copyコンストラクターにアクセスできないが、スローする可能性のあるmoveコンストラクターにアクセスできる場合は、強力な例外保証免除されます)。

noexceptはthrow()の改良版であり、C++ 11では非推奨です。 throw()とは異なり、noexceptはstd :: unexpectedを呼び出さず、スタックを巻き戻す場合と巻き戻さない場合があります。これにより、コンパイラは、throw()の実行時オーバーヘッドなしでnoexceptを実装できる可能性があります。

詳細については、以下のWebサイトをご覧ください。

編集:上記のポイントを説明するためのサンプルソースコード。

// whether foo is declared noexcept depends on if the expression
// T() will throw any exceptions, check in compile time
template <class T>
void foo() noexcept(noexcept(T())) {     
}

void bar() noexcept(true) {    
}

void baz() noexcept {
    throw 42;     
}  // noexcept is the same as noexcept(true)

int main() 
{
    foo<int>();  // noexcept(noexcept(int())) => noexcept(true), so this is fine

    bar();  // fine
    baz();  // compiles, but at runtime this calls std::terminate
}
12
Validus Oculus

noexceptは、関数が例外をスローしないように意図されていることを示します。これは、開発者が提供する保証であり、コンパイラーによって強制されません。したがって、自分でキャッチしない例外をスローする可能性のある関数を関数が呼び出す状況でこれを使用するのは悪いことです。

例外指定子がC++で最適ではなかったため、throw()指定子の全範囲が削除されました。以下を参照してください。 C++ 03 throw()指定子C++ 11 noexceptの違い

noexceptには、どの例外がスローされるかではなく、例外がスローされるかどうかを示すという利点があります。関数が例外をスローすることが予想される場合は、falseのパラメーターを受け入れます。

これの使用法は、たとえば、あるスーパークラスが継承されたクラスに「強制」したい継承されたクラス構造で、特定の仮想関数が例外をスローすることを許可されない場合があります。さらに、コンパイラは最適化のために情報を使用する場合があります。

noexceptは、§5.3.7に従って、式を評価し、その式が例外をスローする可能性があるかどうかを返すことができる演算子でもあります。

5.3.7noexcept演算子[expr.unary.noexcept]

1 noexcept演算子は、未評価のオペランドであるオペランドの評価(5節)が例外をスローできるかどうかを判別します(15.1)。 noexcept-式:noexcept(式)

2 noexcept演算子の結果は、bool型の定数であり、右辺値です。

3評価される可能性のあるコンテキストで式に含まれる場合、noexcept演算子の結果はfalseです。

—呼び出しが定数式(5.19)でない限り、スローしない例外仕様(15.4)を持たない、関数、メンバー関数、関数ポインター、またはメンバー関数ポインターへの潜在的に評価される呼び出し。
—潜在的に評価されたスロー式(15.1)、
—潜在的に評価されるdynamic_cast式dynamic_cast(v)。ここで、Tは参照型であり、実行時チェック(5.2.7)が必要です。
—タイプがポリモーフィッククラスタイプ(10.3)であるglvalue式に適用される潜在的に評価されたtypeid式(5.2.8)。
それ以外の場合、結果は真です。

可能な最適化とScottMeyersについて説明することはできません: http://aristeia.com/EC++11-14/noexcept%202014-03-31.pdf 彼のブログ投稿から: 可能な場合はいつでも関数noexceptを宣言しますか?

コールスタックの巻き戻しと場合によっては巻き戻しの違いは、コード生成に驚くほど大きな影響を与えます。 noexcept関数では、オプティマイザーは、例外が関数から伝播する場合にランタイムスタックを巻き戻し可能な状態に保つ必要はありません。また、例外が関数を離れた場合に、noexcept関数のオブジェクトが構築の逆順序で破棄されるようにする必要もありません。 。 その結果、noexcept関数の本体内だけでなく、関数が呼び出されるサイトでも、最適化の機会が増えます。このような柔軟性は、noexcept関数にのみ存在します。 「throw()」例外仕様のある関数は、例外仕様のない関数と同様に、それを欠いています。

7
Tommy Andersen

私はあなたの問題を説明するために2つのコードを投稿しています:

コード1:

_#include <iostream>
using namespace std;
void foo() noexcept     // see the noexcept specifier
{
    throw 42;
}
int main()
{
    try
    {
        foo();
    }
    catch(...)
    {
        cerr<<"exception caught\n";
    }
    return 0;
}
_

ここでの出力は次のようになります:-

_terminate called after throwing an instance of 'int'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
_

noexceptthenを削除すると:

コード2:

_#include <iostream>
using namespace std;
void foo()    // noexcept is eliminated
{
    throw 42;
}
int main()
{
    try
    {
        foo();
    }
    catch(...)
    {
        cerr<<"exception caught\n";
    }
    return 0;
}
_

出力は次のようになります:-

_exception caught
_

foonoexceptとして署名されているため、terminateが呼び出されました。

継承コンストラクターと暗黙的に宣言されたデフォルトコンストラクター、コピーコンストラクター、移動コンストラクター、デストラクタ、コピー割り当て演算子、および移動割り当て演算子は、次の関数を呼び出す必要がない限り、デフォルトですべてnoexcept(true)です。 noexcept(false)、この場合、これらの関数はnoexcept(false)です。

次のような行を書くこともできます。

_cout << boolalpha << noexcept(foo);   // here noexcept acts as 
                                     // an operator instead of a specifier
_

上記の行は、fooexceptionをスローするかどうかをチェックします。スローされる場合、戻り値はtrue else falseになります。

これらについて詳しくは、こちらをご覧ください: http://scottmeyers.blogspot.dk/2014/03/declare-functions-noexcept-whenever.html

5
CppNITR