メインコンテンツ

AUTOSAR C++14 Rule A6-2-1

Move and copy assignment operators shall either move or respectively copy base classes and data members of a class, without any side effects

説明

ルール定義

Move and copy assignment operators shall either move or respectively copy base classes and data members of a class, without any side effects.

根拠

移動代入演算子とコピー代入演算子を使用するときは、演算によって、二次的影響なしにソース オブジェクトからターゲット オブジェクトに移動またはコピーされることが想定されます。クラスの移動代入演算子またはコピー代入演算子に二次的影響があると、移動代入中またはコピー代入中にオブジェクトの不変条件が変更される可能性があります。クラス C の複数のオブジェクトが互いにコピー代入される次のコードを考えます。

class C{
	//...
	C& operator=(const C& oth) { 
		value = oth.value / 2;
		return *this;		
	}
public:
	int value;
};

int main(){
	C a, b, c; 
	a.value = 48;
	// …
	b = a; // b.m = 24  
	c = b; // c.m = 12
	a = c; // a.m = 6
}
各コピー代入中に、ターゲット オブジェクトの value がソース オブジェクトの value の半分になります。3 つの連続するコピー代入演算が成功すると a.value6 になりますが、これは想定外です。アルゴリズムでは、移動代入演算子とコピー代入演算子はオブジェクトの不変条件を変更しないと想定します。クラスの移動代入演算子とコピー代入演算子にオブジェクトの不変条件を変更する二次的影響があると、標準テンプレート ライブラリ (STL) などのアルゴリズム ライブラリを使用したときに想定外の結果が生じる可能性があります。

移動代入とコピー代入はコード内で多数回使用されるので、二次的影響が生じるコードは、コードの実行が遅くなり、リソースが集約的になる可能性があります。移動代入演算子では、二次的影響が生じるコードがあるとコンパイラはすべての代入にコピー演算を使用する可能性があり、これが非効率になります。

移動代入中およびコピー代入中に最適化されて信頼性の高い性能を維持するには、移動代入演算子とコピー代入演算子で次の演算のみを実行します。

  • データ メンバーと基底クラスをコピーまたは移動する

  • ポインター *this を返す

  • 可能な場合は、移動元オブジェクトを有効な状態に設定する

無関係な二次的影響やパフォーマンス オーバーヘッドを生じさせる不必要なコードは避けてください。

Polyspace 実装

Polyspace® は、コピー代入演算子または移動代入演算子の本体内の次の操作にフラグを設定しません。

  • コピー代入または移動代入。

  • 関係演算または比較演算。

  • 移動演算でのソース オブジェクトの変更。

  • 関数 std::swap または等価なユーザー定義 noexcept スワップ関数の呼び出し。Polyspace はシグネチャが void T::swap(T&) または void [N::]swap(T&, T&) である関数をスワップ関数として識別します。最初のシグネチャは、1 つの引数を取るクラス T のメンバー関数を表します。2 番目のシグネチャは、2 つの引数を取る名前空間 N 内の非メンバー関数または静的関数を表します。swap という名前の大文字と小文字は区別されず、先頭または末尾にアンダースコアを付加できます。

  • 変数 static の代入と変更。

Polyspace はコピー代入演算子内または移動代入演算子内のこれ以外の演算に対して、望ましくない二次的影響としてフラグを設定します。たとえば、ユーザー定義スワップ関数が noexcept ではない場合、そのスワップ関数の呼び出しは望ましくない二次的影響と見なされます。コピー コンストラクターと移動コンストラクターに対する同様のルールについては、AUTOSAR C++14 Rule A12-8-1 を参照してください。

トラブルシューティング

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

すべて展開する

次のコードは、二次的影響がある移動代入演算子とコピー代入演算子に Polyspace がどのようにフラグを設定するかを示します。

#include<cstdint>
#include<iostream>
class B
{
public:
	B() : ptr(0) {}
	B& operator=(B&& oth) //Noncompliant
	{

		if(&oth == this) {
			return *this;
		}
		ptr = std::move(oth.ptr); 
		std::cout<<"Moved";
		return *this;
	}

private:
	std::int32_t* ptr;
};
class C
{
public:
	C(int t=0) : x(t) {}
	C& operator=(const C& oth)  // Noncompliant
	{
		if(&oth == this) {
			return *this;
		}
		x = oth.x % 2;         // This operation produces side-effect
		count++; //Not a side effect
		return *this;
	}

private:
	std::int32_t x;
	static std::int32_t count;

};
class D
{
public:
	D(const D&) = default;
	D(D&&) = default;

	D& operator=(const D& oth) & {     // Noncompliant
		D tmp(oth);
		swap(tmp);
		return *this;
	}
	
	// Member function swap
	void swap(D& rhs)  {
		//...
	}

private:
	std::int32_t x;
};
  • 二次的影響として、クラス B の移動代入演算子は出力ストリームに文字列を出力します。この二次的影響は移動演算にパフォーマンス オーバーヘッドを生じさせます。このステートメント std::cout<<"Moved" が例外を引き起こすと、コードの実行に想定外の停止が生じる可能性があります。Polyspace は移動代入演算子にフラグを設定し、ステートメントを強調表示します。

  • C のコピー代入演算子はソース オブジェクトのデータ メンバー x を変更します。この二次的影響はパフォーマンス オーバーヘッドを生じさせます。移動演算中およびコピー演算中にデータ メンバーへの想定外の変更があると、コードを標準テンプレート ライブラリと非互換にし、開発中のエラーを生じさせます。Polyspace はコピー代入演算子にフラグを設定し、ステートメント x = oth.x % 2 を強調表示します。静的変数 count のインクリメントは二次的影響ではありません。

  • クラス D のコピー代入演算子は、_swap_ というユーザー定義スワップ関数を呼び出します。このスワップ関数は noexcept ではありません。_swap_ から例外が発生すると、この例外はコピー代入演算子の想定外の二次的影響になります。Polyspace は、コピー コンストラクターがこのルールに非準拠としてフラグを設定します。noexcept として指定されたユーザー定義スワップ関数を使用してください。

チェック情報

グループ: ステートメント
カテゴリ: Required、Automated

バージョン履歴

R2020b で導入