15 Special member functions [special]

15.8 Copying and moving class objects [class.copy]

15.8.3 Copy/move elision [class.copy.elision]

При соблюдении определенных критериев реализации разрешается опускать конструкцию копирования / перемещения объекта класса, даже если конструктор, выбранный для операции копирования / перемещения, и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения как просто два разных способа ссылки на один и тот же объект. Если первым параметром выбранного конструктора является ссылка rvalue на тип объекта, уничтожение этого объекта происходит тогда, когда цель была бы уничтожена; в противном случае разрушение происходит в более позднее время, когда два объекта были бы уничтожены без оптимизации.122 Это вызванное исключение операций копирования / перемещения разрешено в следующих случаях (которые могут быть объединены для исключения нескольких копий):copy elision

  • в return операторе функции с типом возвращаемого класса, когда expressionэто имя энергонезависимого автоматического объекта (кроме параметра функции или переменной, введенной с помощью exception-declarationa handler([except.handle])) с тем же типом (игнорируя cv-квалификацию ) в качестве возвращаемого типа функции, операцию копирования / перемещения можно опустить, сконструировав автоматический объект непосредственно в возвращаемом объекте вызова функции.

  • в a throw-expression, когда операнд является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause), область действия которого не выходит за пределы самого внутреннего охватывающего объекта try-block(если он есть), копирование / перемещение Операция от операнда до exception object может быть опущена путем создания автоматического объекта непосредственно в объекте исключения

  • когда exception-declarationобработчик исключения (Clause [except]) объявляет объект того же типа (за исключением cv-qualification), что и объект exception object, операция копирования может быть опущена, рассматривая exception-declarationкак псевдоним для объекта исключения, если значение программы будет без изменений, за исключением выполнения конструкторов и деструкторов для объекта, объявленного классом exception-declaration. [ Note: Не может быть перехода от объекта исключения, потому что это всегда lvalue. ] end note

Копирование требуется, когда выражение оценивается в контексте, требующем a constant expression и in constant initialization. [ Note: Копирование может не выполняться, если то же выражение оценивается в другом контексте. ]end note

[Example:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};

Thing f() {
  Thing t;
  return t;
}

Thing t2 = f();

struct A {
  void *p;
  constexpr A(): p(this) {}
};

constexpr A g() {
  A a;
  return a;
}

constexpr A a;          // well-formed, a.p points to a
constexpr A b = g();    // well-formed, b.p points to b

void g() {
  A c = g();            // well-formed, c.p may point to c or to an ephemeral temporary
}

Здесь критерии исключения могут исключить копирование локального автоматического объекта t в объект результата для вызова функции f(), который является глобальным объектом t2. Фактически создание локального объекта t можно рассматривать как прямую инициализацию глобального объекта t2, и уничтожение этого объекта произойдет при выходе из программы. Добавление конструктора перемещения в Thing имеет тот же эффект, но t2 исключается именно конструкция перемещения от локального автоматического объекта к нему. ]end example

В следующих контекстах инициализации копирования вместо операции копирования может использоваться операция перемещения:

  • Если expressionin a return statement - это (возможно id-expression , заключенное в скобки), которое именует объект с автоматической продолжительностью хранения, объявленной в теле или parameter-declaration-clauseсамой внутренней включающей функции, или lambda-expression, или

  • если операнд a throw-expression является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause), область действия которого не выходит за пределы самого внутреннего охватывающего объекта try-block(если он есть),

разрешение перегрузки для выбора конструктора для копии сначала выполняется, как если бы объект был обозначен rvalue. Если первое разрешение перегрузки завершилось неудачно или не было выполнено, или если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, с квалификацией cv), разрешение перегрузки выполняется снова, рассматривая объект как lvalue. [ Note: Это двухэтапное разрешение перегрузки должно выполняться независимо от того, произойдет ли исключение копирования. Он определяет конструктор, который будет вызываться, если исключение не выполнено, и выбранный конструктор должен быть доступен, даже если вызов отклонен. ]end note

[Example:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(Thing&&);
private:
  Thing(const Thing&);
};

Thing f(bool b) {
  Thing t;
  if (b)
    throw t;            // OK: Thing(Thing&&) used (or elided) to throw t
  return t;             // OK: Thing(Thing&&) used (or elided) to return t
}

Thing t2 = f(false);    // OK: no extra copy/move performed, t2 constructed by call to f

struct Weird {
  Weird();
  Weird(Weird&);
};

Weird g() {
  Weird w;
  return w;             // OK: first overload resolution fails, second overload resolution selects Weird(Weird&)
}

end example]

Поскольку уничтожается только один объект вместо двух, и один конструктор копирования / перемещения не выполняется, остается один объект, уничтоженный для каждого созданного объекта.