AUTOSAR C++14 Rule A12-8-2

Определяемые пользователем операторы копирования и переноса должны использовать определяемую пользователем функцию no-throw swap

Описание

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

Определяемые пользователем операторы копирования и переноса должны использовать определяемую пользователем функцию no-throw swap.

Объяснение

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

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 функции swap.

Реализация 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 использует реализацию копирования и замены, но использует стандартную 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 функция, которая принимает два аргумента. Потому что функция swap задана как нестатическая функция представителя от EPolyspace ожидает E::swap функция, имеющая только один аргумент. Polyspace помечает копию и перемещает операторов E поскольку функция swap имеет неожиданную сигнатуру.

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

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