exponenta event banner

Правило AUTOSAR C++ 14 A12-8-2

Определяемые пользователем операторы назначения копирования и перемещения должны использовать определяемую пользователем функцию без замены

Описание

Определение правила

Определяемые пользователем операторы назначения копирования и перемещения должны использовать определяемую пользователем функцию без замены.

Объяснение

Наивный оператор назначения копирования или перемещения, реализованный без использования функции подкачки, может следовать шаблону в этом коде:

class A{
	//...
	A & operator=(const A & rhs)
	{
		if (this != &rhs) // check for self assignment
		{
			// release resource in lhs
			
			// Allocate resource for modified lhs
			
			
			// Copy or move the resources from rhs to lhs
			
		}

		return *this;
	}
private:
	//resources
	int* mArray;
};
Такая наивная реализация оператора назначения копирования или перемещения не может обеспечить надежную безопасность исключения, поскольку, если какая-либо из операций вызывает исключение, левый операнд не может быть возвращен в исходное состояние. Предшествующий шаблон также неэффективен, поскольку требует проверки для самостоятельного назначения. Дублирование кода между таким оператором назначения копирования или перемещения и конструктором копирования или перемещения затрудняет ведение кода.

Для решения этих проблем используйте определяемые пользователем swap функции, которые не вызывают исключений. Рассмотрим следующий пример:

class A{
	//...
	A & operator=(A rhs)
	{
		Swap(*this,rhs);
	}
	friend void Swap(A& lhs, A& rhs) noexcept{
		//...
	}
private:
	//resources
	int* mArray;
	
};
Эта реализация оператора назначения копирования или перемещения не пытается выделить или освободить память. Вместо этого он обменивается ресурсами между левым и правым операндом, вызывая определяемый пользователем noexcept функция Swap. Это Swap функция может быть реализована с использованием std::swap функция. Преимущества этой модели:

  • Сильная безопасность исключения: эта реализация оператора назначения копирования или перемещения берет временную копию правого операнда с помощью конструктора копирования или перемещения и заменяет временную копию левым операндом. Поскольку функции перемещения и замены должны быть noexcept, только операция копирования может вызвать исключение. Если этот оператор вызывает исключение, то недействительной может быть только временная копия правого операнда. Состояние правого или левого операнда остается нетронутым.

  • Повторное использование кода: в этой реализации оператор назначения копирования или перемещения повторно использует конструктор копирования или перемещения. Специфичный для класса swap функция также может быть повторно использована для реализации других алгоритмов.

  • Эффективность: За счет исключения проверки по самостоятельному назначению, оператор является более эффективным.

Для реализации оператора присвоения копирования или перемещения используйте определяемые пользователем noexcet функции подкачки.

Внедрение Polyspace

Polyspace ® помечает оператор назначения копирования или перемещения, если он не содержит хотя бы один вызов определяемой пользователем функции подкачки. Polyspace идентифицирует функции, имеющие эти подписи, как функции подкачки :void T::swap(T&) или void [N::]swap(T&, T&). Первая сигнатура представляет функцию-член класса T для этого требуется один аргумент. Вторая сигнатура представляет не являющуюся членом или статическую функцию в пространстве имен N для этого требуется два аргумента. Имя swap может быть без учета регистра и префиксом или постфиксированным подчеркиванием.

Поиск неисправностей

Если вы ожидаете нарушения правила, но не видите его, обратитесь к разделу Нарушения стандартов кодирования не отображаются.

Примеры

развернуть все

#include <utility>
#include <string>
class B
{
  public:
    B& operator=(const B& oth) & { // Noncompliant
      if (this != &oth)
      {
        ptr1 = new std::int32_t(*oth.ptr1);
        ptr2 = new std::int32_t(
          *oth.ptr2); // Exception thrown here results in
        // a memory leak of ptr1
      }

      return *this;
    }
    B& operator=(B&& oth) & noexcept { // Noncompliant
      if (this != &oth)
      {
        ptr1 = std::move(oth.ptr1);
        ptr2 = std::move(oth.ptr2);
        oth.ptr1 = nullptr;
        oth.ptr2 = nullptr;
      }

      return *this;
    }
private:
    std::int32_t* ptr1;
    std::int32_t* ptr2;
};

В этом примере оператор назначения копирования и перемещения для класса B использует наивную реализацию вместо реализации копирования и замены. Оператор копирования и перемещения B неэффективен и не обеспечивает надежной безопасности за исключением случаев. Polyspace помечает эти операторы как несоответствующие.

#include <utility>
#include <string>
class C
{
  public:
    C(const C&) = default;
    C(C&&) = default;

    C& operator=(const C& oth) & {     //Noncompliant
      C tmp(oth);
      std::swap(ptr1, tmp.ptr1);       
      return *this;
    }
    C& operator=(C&& oth) & {          // Noncompliant
      C tmp(std::move(oth));
      std::swap(ptr1, tmp.ptr1);       
      return *this;
    }

  private:
    std::int32_t* ptr1;
};

В этом примере оператор назначения копирования и перемещения для класса C использует реализацию copy-and-swap, но использует стандарт std::swap функция вместо специфичной для класса, определяемой пользователем swap функция. Потому что класс C требуется пользовательский оператор копирования и перемещения, также требуется пользовательский оператор swap функция. Polyspace помечает операторы как несоответствующие.

#include <utility>
#include <string>

class D
{
  public:
    D(const D&) = default;
    D(D&&) = default;
    D& operator=(const D& oth) & {     // Noncompliant
      D tmp(oth);
      _swap_(*this,tmp);
      return *this;
    }
    D& operator=(D&& oth) & {          // Noncompliant
      D tmp(std::move(oth));
      _swap_(*this,tmp);
      return *this;
    }
	//...
	friend void _swap_(D& lhs, D& rhs){ // swap function not noexcept
	//...
	}
};

В этом примере оператор назначения копирования и перемещения для класса D использует swap функция, которая не является noexcept. Эти операторы не обеспечивают надежную защиту от исключений. Polyspace помечает их как несоответствующие.

#include <utility>
#include <string>

class E
{
  public:
    E(const E&) = default;
    E(E&&) = default;

    E& operator=(const E& oth) & {     // Noncompliant
      E tmp(oth);
      swap(*this,tmp);
      return *this;
    }
    E& operator=(E&& oth) & {          // Noncompliant
      E tmp(std::move(oth));
      swap(*this,tmp);
      return *this;
    }

    // Member function swap
    void swap(E& lhs, E& rhs) noexcept {
      std::swap(lhs.ptr1, rhs.ptr1);
      std::swap(lhs.ptr2, rhs.ptr2);
    }

  private:
    std::int32_t* ptr1;
    std::int32_t* ptr2;
};

В этом примере оператор назначения копирования и перемещения для класса E использует swap функция, которая принимает два аргумента. Поскольку функция подкачки определена как нестатическая функция-член E, Polyspace ожидает E::swap чтобы иметь только один аргумент. Polyspace помечает операторы копирования и перемещения E поскольку функция подкачки имеет неожиданную подпись.

Проверить информацию

Группа: Специальные функции-члены
Категория: Консультации, Автоматизированные
Представлен в R2021a