メインコンテンツ

AUTOSAR C++14 Rule A15-5-1

All user-provided class destructors, deallocation functions, move constructors, move assignment operators and swap functions shall not exit with an exception.A noexcept exception specification shall be added to these functions as appropriate

説明

ルール定義

All user-provided class destructors, deallocation functions, move constructors, move assignment operators and swap functions shall not exit with an exception.A noexcept exception specification shall be added to these functions as appropriate.

根拠

このルールは、特定の関数が例外で終了してはならないとしています。

  • デストラクターと割り当て解除関数:例外が発生すると、コンパイラは、デスクトラクターと割り当て解除関数を呼び出して、スタック内のオブジェクトを安全に削除します。その時点で、デストラクターまたは割り当て解除関数が例外で終了した場合は、コンパイラがプログラム実行を異常終了します。使用しているハードウェアまたはソフトウェアに応じて、プログラムの異常終了がリソース リークとセキュリティの脆弱性につながる可能性があります。このような問題を避けるには、例外で終了する可能性のあるデストラクターと deallocator 関数を使用しないようにします。既定のデストラクターと deallocator 関数は、noexcept 関数です。カスタム デストラクターまたは割り当て解除関数を指定する場合は、それらを noexcept として指定し、関数内ですべての例外を処理し、例外で終了しないようにします。ポリモーフィック クラス階層の場合は、このルールが基底クラスとすべての派生クラスのデストラクターに適用されます。

  • 移動コンストラクターと移動代入演算子:移動コンストラクターまたは移動代入演算子が例外で終了する場合は、プログラムが移動操作前の状態に戻ることを保証できません。例外で終了する可能性のある移動コンストラクターまたは移動代入演算子は使用しないでください。このような関数は noexcept として指定します。これは、標準ライブラリ関数が noexcept として宣言されていない移動操作を回避する可能性があるためです。これらの特殊なメンバー関数を =default として宣言することもできます。特殊なメンバー関数を =default として宣言できる場合の詳細については、AUTOSAR C++14 Rule A12-0-1を参照してください。

  • スワップ関数:開発者は、スワップ関数が例外で終了することを想定していません。スワップ関数が例外で終了する場合は、標準ライブラリ アルゴリズムとコピー演算が想定どおりにコード内で動作しない可能性があります。スワップ関数を noexcept として指定します。スワップ関数内で例外で終了する可能性がある演算は回避します。

テンプレートを汎用移動コンストラクター、汎用移動代入演算子、および汎用スワップ関数として使用する場合は、これらのテンプレートに、このルールに違反することなく、動的例外指定を含めることができます。

Polyspace 実装

Polyspace® は、例外を発生させる可能性のあるユーザー定義のデストラクター、割り当て解除関数、移動コンストラクター、移動代入演算子、およびスワップ関数にフラグを設定します。関数の名前が swap または Swap で、入力として参照を取る場合は、Polyspace が、それがスワップ関数であると見なします。

Polyspace は、宣言されているが定義されていない関数を無視します。

トラブルシューティング

ルール違反が想定されるものの、Polyspace から報告されない場合は、コーディング規約違反が想定どおりに表示されない理由の診断を参照してください。

すべて展開する

この例では、Polyspace が、例外を発生させる可能性のあるデストラクターと割り当て解除関数にどのようにフラグを設定するかを示します。2 つのクラスを含む次のコードについて考えます。

#include <stdexcept>
class Compliant
{
public:
	//...
	~Compliant()  //Compliant                            
	{
		try {
			// ...
			throw std::runtime_error("Error"); 
		}
		catch (std::exception& e) {
		//...	
		}
	}
};

class Noncompliant
{
public:
	//...
	~Noncompliant() 
	{
		throw std::runtime_error("Error"); //Noncompliant    
	}
	static void operator delete(void* ptr, std::size_t sz) 
	{
		// ...
		throw std::runtime_error("Error");    // Noncompliant
	}
};
  • Compliant のデストラクターは、throw ステートメントを使用することによって、例外を発生させます。この例外はデストラクター関数内で try-catch ブロックを使用して処理されるため、~Compliant() はこのルールに準拠しています。

  • Noncompliant のデストラクターは、throw ステートメントを使用することによっても、例外を発生させます。この例外は関数内で処理されないため、デストラクター ~Noncompliant() は例外で終了します。Polyspace は、デストラクター内のこの throw ステートメントにフラグを設定します。

  • 割り当て解除関数 Noncompliant::delete() は、関数内で発生した例外を処理しないため、このルールに準拠していません。Polyspace は、関数内の throw ステートメントにフラグを設定します。

Polyspace は、以下の場合に、移動演算子または移動コンストラクターにフラグを設定します。

  • 例外で終了する可能性がある

  • noexcept として指定されていない

移動演算が 2 つのクラスに実装されている次のコードを考えます。

#include <stdexcept>
class Compliant
{
	//...
public:
	Compliant(Compliant&& rhs) noexcept   //Compliant          
	{
		try {
			// ...
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
			//...
		}
	}

	Compliant& operator=(Compliant&& rhs) noexcept //Compliant   
	{
		try {
			// ...
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
			//...
		}
		return *this;
	}	
};

class Noncompliant
{
public:
	//...
	Noncompliant(Noncompliant&& rhs) //Noncompliant 
	{
		// ...
		throw std::runtime_error("Error");    //Noncompliant
	}
	Noncompliant& operator=(Noncompliant&& rhs) //Noncompliant
	{
		// ...
		throw std::runtime_error("Error");   //Noncompliant
		return *this;
	}

};

  • クラス Compliant の移動代入演算子と移動コンストラクターは noexcept として指定され、これらの関数がその中で発生した例外を処理します。Compliant の移動コンストラクターと移動代入演算子は、このルールに準拠しています。

  • クラス Noncompliant の移動代入演算子と移動コンストラクターは noexcept として指定されません。Polyspace は、これらの関数の宣言にフラグを設定します。

  • クラス Noncompliant の移動代入演算子と移動コンストラクターには、これらの関数内で処理されない例外を発生させる throw ステートメントが含まれています。Polyspace は、これらの throw ステートメントにフラグを設定します。

2 つのスワップ関数を含む次のコードについて考えます。

#include <stdexcept>
namespace Compliant{
	class C1{};
	void Swap(C1& lhs, C1& rhs) noexcept   //Compliant
	{
		// Implementation
	}
}
namespace Noncompliant{
	class C2{};
	void Swap( C2& lhs, C2& rhs ) noexcept(false) //Noncompliant
	{
		throw std::runtime_error( "Error" );   //Noncompliant
	}
}
  • 関数 Compliant::Swap() は、noexcept として指定され、例外を発生させません。このスワップ関数はこのルールに準拠しています。

  • 関数 Noncompliant::Swap() は、noexcept(false) として指定され、例外で終了します。Polyspace は、この関数と throw ステートメントの例外指定にフラグを設定します。

チェック情報

グループ: Exception handling
カテゴリ: Required、Automated

バージョン履歴

R2020b で導入