15 Special member functions [special]

default constructor, copy constructor И copy assignment operator, move constructor и move assignment operator, и destructor являются special member functions. [ Note: Реализация неявно объявляет эти функции-члены для некоторых типов классов, если программа не объявляет их явно. Реализация неявно определит их, если они есть odr-used. Смотрите [class.ctor], [class.dtor] и [class.copy]. ] Неявно объявлена специальная функция - член объявляется при закрытии из . Программы не должны определять неявно объявленные специальные функции-члены. end note} class-specifier

Программы могут явно ссылаться на неявно объявленные специальные функции-члены. [ Example: Программа может явно вызывать, принимать адрес или формировать указатель на член неявно объявленной специальной функции-члена.

struct A { };                   // implicitly declared A​::​operator=
struct B : A {
  B& operator=(const B &);
};
B& B::operator=(const B& s) {
  this->A::operator=(s);        // well formed
  return *this;
}

end example]

[ Note: Специальные функции-члены влияют на способ создания, копирования, перемещения и уничтожения объектов типа класса, а также на то, как значения могут быть преобразованы в значения других типов. Часто такие специальные функции-члены вызываются неявно. ]end note

Специальные функции-члены подчиняются обычным access rules. [ Example: Объявление конструктора protected гарантирует, что только производные классы и друзья могут создавать объекты с его помощью. ]end example

Для класса его нестатические члены-данные, его невиртуальные прямые базовые классы и, если класс не является abstract, его виртуальные базовые классы называются его potentially constructed subobjects.

15.1 Constructors [class.ctor]

У конструкторов нет имен. В объявлении конструктора declarator- это function declarator форма

ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

где ptr-declaratorсостоит исключительно из id-expression, необязательной attribute-specifier-seqи необязательной окружающих скобок, и id-expressionимеет одну из следующих форм:

Не class-nameдолжно быть typedef-name. В объявлении конструктора, каждый decl-specifierв необязательном decl-specifier-seqдолжно быть friend, inline, explicitили constexpr. [Example:

struct S {
  S();              // declares the constructor
};

S::S() { }          // defines the constructor

end example]

Конструктор используется для инициализации объектов своего типа класса. Поскольку у конструкторов нет имен, они никогда не обнаруживаются при поиске имени; однако explicit type conversion using the functional notation вызовет конструктор для инициализации объекта. [ Note: Для инициализации объектов типа класса см [class.init]. ]end note

Конструктор может быть вызван для const, volatile или const volatile объекта. const и volatile семантика ([dcl.type.cv]) не применяются к строящемуся объекту. Они вступают в силу, когда конструктор most derived object заканчивается.

default Конструктор класса X является конструктор класса ,X для которого каждый параметр , который не является параметром функции по умолчанию пакет имеет аргумент в (включая случай конструктора без параметров). Если для класса нет объявленного пользователем конструктора X, неявный конструктор, не имеющий параметров, неявно объявляется как заданный по умолчанию ([dcl.fct.def]). Неявно объявленный конструктор по умолчанию является inline public членом своего класса.

Конструктор по умолчанию для класса X определяется как удаленный, если:

  • X представляет собой объединение, которое имеет вариантный член с нетривиальным конструктором по умолчанию, и ни один вариантный член не X имеет инициализатора члена по умолчанию,

  • X является классом без объединения, который имеет вариантный член M с нетривиальным конструктором по умолчанию, и ни один вариантный член анонимного объединения, содержащий его, не M имеет инициализатора члена по умолчанию,

  • любой нестатический элемент данных без no default member initializer относится к ссылочному типу,

  • любой невариантный нестатический член данных типа с квалификацией const (или его массив) без no brace-or-equal-initializerне имеет конструктора по умолчанию, предоставленного пользователем,

  • X является объединением, и все его варианты членов относятся к типу с квалификатором const (или его массиву),

  • X не является классом объединения, и все члены любого анонимного члена объединения относятся к типу с квалификацией const (или его массиву),

  • любой потенциально сконструированный подобъект, за исключением нестатического члена данных с a brace-or-equal-initializer, имеет тип класса M (или его массив) и либо M не имеет конструктора по умолчанию, либо overload resolution применительно к Mсоответствующему конструктору find приводит к неоднозначности или к функции, которая удаляется или недоступен из конструктора по умолчанию, или

  • любой потенциально созданный подобъект имеет тип с деструктором, который удален или недоступен из конструктора по умолчанию по умолчанию.

Конструктор по умолчанию - trivial это если он не предоставлен пользователем и если:

  • у его класса нет virtual functions и нет virtual base classes, и

  • ни один нестатический член данных его класса не имеет default member initializer, и

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

  • для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый такой класс имеет тривиальный конструктор по умолчанию.

В противном случае конструктором по умолчанию является non-trivial.

Конструктор по умолчанию, который установлен по умолчанию и не определен как удаленный, - implicitly defined это когда он odr-used должен создать объект своего типа класса ([intro.object]) или когда он явно используется по умолчанию после его первого объявления. Неявно определенный конструктор по умолчанию выполняет набор инициализаций класса, которые были бы выполнены написанным пользователем конструктором по умолчанию для этого класса с no ctor-initializerи пустым compound-statement. Если этот пользовательский конструктор по умолчанию будет иметь неправильный формат, значит, программа будет иметь неправильный формат. Если этот написанный пользователем конструктор по умолчанию будет удовлетворять требованиям a constexpr constructor, то неявно определенный конструктор по умолчанию будет constexpr. Прежде чем конструктор по умолчанию по умолчанию для класса будет неявно определен, все не предоставленные пользователем конструкторы по умолчанию для его базовых классов и его нестатические элементы данных должны быть неявно определены. [ Note: Неявно объявленный конструктор по умолчанию имеет спецификацию исключения ([except.spec]). Явно заданное по умолчанию определение может иметь неявную спецификацию исключения, см [dcl.fct.def]. ]end note

Конструкторы по умолчанию вызывается неявно создавать объекты класса статики, нити или автоматическую продолжительность хранения ([basic.stc.static], [basic.stc.thread], [basic.stc.auto]) , определенной без initializer, призвано создавать объекты классовых , dynamic storage duration созданные new-expression в котором new-initializer опущено ([expr.new]), или вызываются , когда explicit type conversion синтаксис используется. Программа плохо сформирована, если конструктор по умолчанию для объекта неявно используется, а конструктор - нет accessible.

[ описывает порядок, в котором вызываются конструкторы для базовых классов и нестатических членов данных, и описывает, как можно указать аргументы для вызовов этих конструкторов. ]Note: [class.base.init] end note

return Оператор в теле конструктора не должен указать возвращаемое значение. Адрес конструктора не принимается.

A functional notation type conversion можно использовать для создания новых объектов своего типа. [ Note: Синтаксис выглядит как явный вызов конструктора. ] [end noteExample:

complex zz = complex(1,2.3);
cprint( complex(7.8,1.2) );

end example]

Созданный таким образом объект не имеет имени. [ Note: [class.temporary] описывает время жизни временных объектов. ] [ Явные вызовы конструкторов не дают значений l, см . ]end noteNote: [basic.lval]end note

[ Некоторые языковые конструкции имеют особую семантику при использовании во время построения; видеть и . ]Note: [class.base.init] [class.cdtor]end note

Если во время конструирования объекта доступ к значению объекта или любого из его подобъектов осуществляется через значение glvalue, которое не получено, прямо или косвенно, изthis указателя конструктора , значение полученного таким образом объекта или подобъекта не указывается. [Example:

struct C;
void no_opt(C*);

struct C {
  int c;
  C() : c(0) { no_opt(this); }
};

const C cobj;

void no_opt(C* cptr) {
  int i = cobj.c * 100;         // value of cobj.c is unspecified
  cptr->c = 1;
  cout << cobj.c * 100          // value of cobj.c is unspecified
       << '\n';
}

extern struct D d;
struct D {
  D(int a) : a(a), b(d.a) {}
  int a, b;
};
D d = D(1);                     // value of d.b is unspecified

end example]

15.2 Temporary objects [class.temporary]

Создаются временные объекты

  • когда prvalue материализуется так, чтобы его можно было использовать как glvalue,

  • при необходимости реализации для передачи или возврата объекта тривиально копируемого типа (см. ниже) и

  • когда throwing an exception. [ Note: Время жизни объектов исключения описано в [except.throw]. ] end note

Даже когда создание временного объекта не оценивается (пункт [expr]), все семантические ограничения должны соблюдаться, как если бы временный объект был создан, а затем уничтожен. [ Note: Это включает в себя accessibility и то, был ли он удален, для выбранного конструктора и для деструктора. Однако в частном случае операнда a decltype-specifier([expr.call]) временное значение не вводится, поэтому вышеизложенное не применяется к такому prvalue. ]end note

Материализация временного объекта обычно откладывается на максимально возможный срок, чтобы избежать создания ненужных временных объектов. [ Note: Материализованы временные объекты:

end note] [ Example: Рассмотрим следующий код:

class X {
public:
  X(int);
  X(const X&);
  X& operator=(const X&);
  ~X();
};

class Y {
public:
  Y(int);
  Y(Y&&);
  ~Y();
};

X f(X);
Y g(Y);

void h() {
  X a(1);
  X b = f(X(2));
  Y c = g(Y(3));
  a = f(a);
}

X(2) строится в пространстве, используемом для хранения f()аргумента, и Y(3) создается в пространстве, используемом для хранения g()аргумента. Точно так же f()результат создается непосредственно в, b а g()результат создается непосредственно в c. С другой стороны, выражение a = f(a) требует временного значения для результата f(a), которое материализуется так, чтобы ссылочный параметр A​::​operator=(const A&) мог связываться с ним. ]end example

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

Когда реализация вводит временный объект класса, который имеет нетривиальный конструктор ([class.ctor], [class.copy]), она должна гарантировать, что конструктор вызывается для временного объекта. Точно так же деструктор должен вызываться для временного с нетривиальным деструктором ([class.dtor]). Временные объекты уничтожаются на последнем этапе оценки того, full-expression что (лексически) содержит точку, в которой они были созданы. Это верно, даже если эта оценка заканчивается выдачей исключения. В стоимости вычисление и побочные эффекты уничтожения временного объекта связаны только с полной экспрессией, а не с каким - либо конкретным подвыражением.

Есть три контекста, в которых временные объекты уничтожаются не в конце полного выражения. Первый контекст - это когда конструктор по умолчанию вызывается для инициализации элемента массива без соответствующего инициализатора ([dcl.init]). Второй контекст - это когда конструктор копирования вызывается для копирования элемента массива при копировании всего массива ([expr.prim.lambda.capture], [class.copy]). В любом случае, если у конструктора есть один или несколько аргументов по умолчанию, уничтожение каждого временного элемента, созданного в аргументе по умолчанию, упорядочивается до создания следующего элемента массива, если таковой имеется.

Третий контекст - это когда ссылка привязана к временному объекту.116 Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом подобъекта, к которому привязана ссылка, сохраняется в течение всего времени существования ссылки, за исключением:

  • Временный объект, связанный со ссылочным параметром в a, function call сохраняется до завершения полного выражения, содержащего вызов.

  • Время жизни временной привязки к возвращаемому значению в функции return statement не продлевается; временное уничтожается в конце полного выражения в операторе возврата.

  • Временная привязка к ссылке в a new-initializerсохраняется до завершения полного выражения, содержащего new-initializer. [Example:

    struct S { int mi; const std::pair<int,int>& mp; };
    S a { 1, {2,3} };
    S* p = new S{ 1, {2,3} };   // Creates dangling reference
    

    end example] [ Note: Это может привести к появлению "висящей" ссылки, и в таком случае рекомендуется, чтобы реализации выдавали предупреждение. ] end note

Уничтожение временного объекта, время жизни которого не продлевается путем привязки к ссылке, выполняется до уничтожения каждого временного объекта, который ранее был сконструирован в том же полном выражении. Если время жизни двух или более временных объектов, к которым привязаны ссылки, заканчивается в одной и той же точке, эти временные объекты уничтожаются в этой точке в порядке, обратном завершению их построения. Кроме того, уничтожение временных связано с ссылками , должны учитывать порядок уничтожения объектов static, threadили automatic storage duration; то есть, если obj1 это объект с той же продолжительностью хранения, что и временный, и созданный до создания временного, временное должно быть уничтожено до того, как obj1 будет уничтожено; Если obj2 это объект с такой же продолжительностью хранения, что и временный, и созданный после создания временного, временное должно быть уничтожено после obj2 уничтожения.

[Example:

struct S {
  S();
  S(int);
  friend S operator+(const S&, const S&);
  ~S();
};
S obj1;
const S& cr = S(16)+S(23);
S obj2;

выражение S(16) + S(23) создает три временныхT1 объекта : первая временная для хранения результата выражения S(16), вторая временная T2 для хранения результата выражения S(23)и третья временная T3 для хранения результата сложения этих двух выражений. Затем временный T3 объект привязывается к ссылке cr. Не указано, был ли созданT1 или T2создан первым. В реализации, T1 которая была создана ранее T2, T2 должна быть уничтожена раньше T1. Временные T1 и T2 привязаны к ссылочным параметрам operator+; эти временные объекты уничтожаются в конце полного выражения, содержащего вызов operator+. Временная T3 привязка к ссылке cr уничтожается в концеcrжизненного цикла , то есть в конце программы. Кроме того, порядок T3 уничтожения учитывает порядок уничтожения других объектов со статической продолжительностью хранения. То есть, потому что obj1 построено раньше T3, и T3 построено раньше obj2, obj2 должно быть уничтожено раньше T3и T3 должно быть уничтожено раньше obj1. ]end example

Те же правила применяются к инициализации initializer_­list объекта ([dcl.init.list]) с его базовым временным массивом.

15.3 Conversions [class.conv]

Преобразование типов объектов класса может определяться конструкторами и функциями преобразования. Эти преобразования вызываются user-defined conversions и используются для неявных преобразований типов (Clause [conv]), для initializationи для явных преобразований типов ([expr.cast], [expr.static.cast]).

Пользовательские преобразования применяются только в том случае, если они однозначны ([class.member.lookup], [class.conv.fct]). Конверсии подчиняются access control rules. Контроль доступа применяется после разрешения неоднозначности ([basic.lookup]).

[ Note: См. [over.match] Обсуждение использования преобразований в вызовах функций, а также примеры ниже. ]end note

Максимум одно определяемое пользователем преобразование (конструктор или функция преобразования) неявно применяется к одному значению. [Example:

struct X {
  operator int();
};

struct Y {
  operator X();
};

Y a;
int b = a;          // error, a.operator X().operator int() not tried
int c = X(a);       // OK: a.operator X().operator int()

end example]

Пользовательские преобразования используются неявно, только если они однозначны. Функция преобразования в производном классе не скрывает функцию преобразования в базовом классе, если две функции не преобразуются в один и тот же тип. Функция overload resolution выбирает лучшую функцию преобразования для выполнения преобразования. [Example:

struct X {
  operator int();
};

struct Y : X {
    operator char();
};

void f(Y& a) {
  if (a) {          // ill-formed: X​::​operator int() or Y​::​operator char()
  }
}

end example]

15.3.1 Conversion by constructor [class.conv.ctor]

Конструктор, объявленный без, function-specifier explicit определяет преобразование типов его параметров (если есть) в тип своего класса. Такой конструктор называется converting constructor. [Example:

struct X {
    X(int);
    X(const char*, int =0);
    X(int, int);
};

void f(X arg) {
  X a = 1;          // a = X(1)
  X b = "Jessie";   // b = X("Jessie",0)
  a = 2;            // a = X(2)
  f(3);             // f(X(3))
  f({1, 2});        // f(X(1,2))
}

end example]

[ Note: Явный конструктор создает объекты так же, как неявные конструкторы, но делает это только там, direct-initialization где явно используются синтаксис или приведение типов ([expr.static.cast], [expr.cast]); см. также [over.match.copy]. Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или value-initialization. [Example:

struct Z {
  explicit Z();
  explicit Z(int);
  explicit Z(int, int);
};

Z a;                            // OK: default-initialization performed
Z b{};                          // OK: direct initialization syntax used
Z c = {};                       // error: copy-list-initialization
Z a1 = 1;                       // error: no implicit conversion
Z a3 = Z(1);                    // OK: direct initialization syntax used
Z a2(1);                        // OK: direct initialization syntax used
Z* p = new Z(1);                // OK: direct initialization syntax used
Z a4 = (Z)1;                    // OK: explicit cast used
Z a5 = static_cast<Z>(1);       // OK: explicit cast used
Z a6 = { 3, 4 };                // error: no implicit conversion

end example] ]end note

Неявный конструктор копирования / перемещения ([class.copy]) - это конструктор преобразования. [ Note: Неявно объявленный конструктор копирования / перемещения не является явным конструктором; он может вызываться для неявного преобразования типов. ]end note

15.3.2 Conversion functions [class.conv.fct]

Функция-член класса X без параметров с именем формы

conversion-function-id:
	operator conversion-type-id
conversion-type-id:
	type-specifier-seq conversion-declaratoropt
conversion-declarator:
	ptr-operator conversion-declaratoropt

указывает преобразование из X в тип, указанный в conversion-type-id. Такие функции называются conversion functions. A decl-specifierв decl-specifier-seq функции преобразования (если есть) не должно быть ни a, defining-type-specifierни static. Тип функции преобразования ([dcl.fct]) - «функция, не возвращающая никаких параметров conversion-type-id». Функция преобразования никогда не используется для преобразования объекта (возможно, квалифицированного cv) в (возможно, квалифицированный cv) объект того же типа (или ссылку на него) в базовый класс (возможно, квалифицированный cv) этого типа (или ссылка на него) или на (возможно cv-квалифицированный) void.117 [Example:

struct X {
  operator int();
  operator auto() -> short;     // error: trailing return type
};

void f(X a) {
  int i = int(a);
  i = (int)a;
  i = a;
}

Во всех трех случаях присвоенное значение будет преобразовано в X​::​operator int(). ]end example

Функция преобразования может быть explicit, и в этом случае она рассматривается только как определяемое пользователем преобразование для direct-initialization. В противном случае пользовательские преобразования не ограничиваются использованием в назначениях и инициализациях. [Example:

class Y { };
struct Z {
  explicit operator Y() const;
};

void h(Z z) {
  Y y1(z);          // OK: direct-initialization
  Y y2 = z;         // ill-formed: copy-initialization
  Y y3 = (Y)z;      // OK: cast notation
}

void g(X a, X b) {
  int i = (a) ? 1+a : 0;
  int j = (a&&b) ? a+b : i;
  if (a) {
  }
}

end example]

Не conversion-type-id должен представлять тип функции или тип массива. In conversion-type-id a conversion-function-id - это самая длинная последовательность токенов, которая могла бы образовать conversion-type-id. [ Note: Это предотвращает двусмысленность между оператором-декларатором * и его эквивалентами-выражениями. [Example:

&ac.operator int*i; // syntax error:
                    // parsed as: &(ac.operator int *)i
                    // not as: &(ac.operator int)*i

Это * декларатор указателя, а не оператор умножения. ] Это правило также предотвращает двусмысленность атрибутов. [end exampleExample:

operator int [[noreturn]] ();   // error: noreturn attribute applied to a type

end example] ]end note

Функции преобразования наследуются.

Функции преобразования могут быть виртуальными.

В шаблоне функции преобразования не должно быть файла deduced return type. [Example:

struct S {
  operator auto() const { return 10; }      // OK
  template<class T>
  operator auto() const { return 1.2; }     // error: conversion function template
};

end example]

Эти преобразования считаются стандартными в целях разрешения перегрузки ([over.best.ics], [over.ics.ref]) и, следовательно, инициализации ([dcl.init]) и explicit casts. Преобразование в void не вызывает никакой функции преобразования ([expr.static.cast]). Даже если они никогда не вызываются напрямую для выполнения преобразования, такие функции преобразования могут быть объявлены и потенциально могут быть достигнуты посредством вызова функции виртуального преобразования в базовом классе.

15.4 Destructors [class.dtor]

В объявлении деструктора declarator- это function declarator форма

ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

где ptr-declaratorсостоит исключительно из id-expression, необязательной attribute-specifier-seqи необязательной окружающих скобок, и id-expressionимеет одну из следующих форм:

Не class-nameдолжно быть typedef-name. Деструктор не должен принимать аргументы ([dcl.fct]). Каждый decl-specifierиз decl-specifier-seq декларации деструктора (если таковые имеются) должны быть friend, inlineили virtual.

Деструктор используется для уничтожения объектов своего типа класса. Адрес деструктора не принимается. Деструктор может быть вызван для , или объекта. и семантика ( ) не применяются к разрушаемому объекту. Они перестают действовать, когда запускается деструктор .constvolatileconst volatileconstvolatile[dcl.type.cv]most derived object

[ Note: Объявление деструктора, не имеющего a, noexcept-specifier имеет такую ​​же спецификацию исключения, как если бы оно было неявно объявлено ([except.spec]). ]end note

Если у класса нет деструктора, объявленного пользователем, деструктор неявно объявляется как defaulted. Неявно объявленный деструктор является inline public членом своего класса.

Деструктор по умолчанию для класса X определяется как удаленный, если:

  • X - подобный объединению класс, который имеет вариантный член с нетривиальным деструктором,

  • любой потенциально созданный подобъект имеет тип класса M (или его массив) и M имеет удаленный деструктор или деструктор, недоступный из деструктора по умолчанию,

  • или, для виртуального деструктора, поиск функции освобождения массива, не связанной с массивом, приводит к неоднозначности или к функции, которая удаляется или недоступна из деструктора по умолчанию.

Деструктор тривиален, если он не предоставлен пользователем и если:

  • деструктор нет virtual,

  • все прямые базовые классы этого класса имеют тривиальные деструкторы, и

  • для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый такой класс имеет тривиальный деструктор.

В противном случае деструктор non-trivial.

Деструктор, который установлен по умолчанию и не определен как удаленный, - implicitly defined это когда он есть odr-used или когда он явно используется по умолчанию после его первого объявления.

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

После выполнения тела деструктора и уничтожения любых автоматических объектов, размещенных в теле, деструктор для класса X вызывает деструкторы для Xпрямых неизменяемых нестатических членов данных, деструкторы для Xневиртуальных прямых базовых классов и, если X это тип наиболее производного класса ([class.base.init]), его деструктор вызывает деструкторы для Xвиртуальных базовых классов. Все деструкторы вызываются так, как если бы на них ссылались с квалифицированным именем, то есть игнорируя любые возможные виртуальные переопределяющие деструкторы в более производных классах. Базы и члены уничтожаются в порядке, обратном завершению их конструктора (см. [class.base.init]). A return statement в деструкторе не может напрямую возвращаться к вызывающей стороне; перед передачей управления вызывающей стороне вызываются деструкторы для членов и баз. Деструкторы для элементов массива вызываются в порядке, обратном их построению (см. [class.init]).

Деструктор может быть объявлен virtual или pure virtual; если в программе создаются какие-либо объекты этого класса или любого производного класса, деструктор должен быть определен. Если у класса есть базовый класс с виртуальным деструктором, его деструктор (объявленный пользователем или неявно объявленный) является виртуальным.

[ Некоторые языковые конструкции имеют особую семантику при использовании во время уничтожения; см . ]Note: [class.cdtor]end note

Деструктор вызывается неявно

В каждом случае контекст вызова - это контекст построения объекта. Деструктор также вызывается неявно через использование a delete-expressionдля сконструированного объекта, выделенного a new-expression; контекст вызова - это delete-expression. [ Note: Массив типа класса содержит несколько подобъектов, для каждого из которых вызывается деструктор. ] Деструктор также может быть вызван явно. Деструктор , если он вызывается или , как указано в , и . Программа плохо сформирована, если деструктор, который потенциально может быть вызван, удален или недоступен из контекста вызова. end note potentially invoked [expr.new] [class.base.init] [except.throw]

В точке определения виртуального деструктора (включая implicit definition) функция освобождения массива, не являющаяся массивом, определяется, как если бы для выражения, delete this появляющегося в невиртуальном деструкторе класса деструктора (см [expr.delete]. Раздел "Ресурсы" ). Если поиск завершился неудачно или функция освобождения имеет значок deleted definition, программа имеет неправильный формат. [ Note: Это гарантирует, что функция освобождения, соответствующая динамическому типу объекта, доступна для delete-expression([class.free]). ]end note

При явном вызове деструктора деструктор указывается с помощью a, ~ за которым следует type-nameили, decltype-specifier что обозначает тип класса деструктора. Вызов деструктора подчиняется обычным правилам для member functions; то есть, если объект не относится к типу класса деструктора и не к классу, производному от типа класса деструктора (в том числе, когда деструктор вызывается через значение нулевого указателя), программа имеет неопределенное поведение. [ Note: Вызов delete нулевого указателя не вызывает деструктор; см [expr.delete]. ] [ end noteExample:

struct B {
  virtual ~B() { }
};
struct D : B {
  ~D() { }
};

D D_object;
typedef B B_alias;
B* B_ptr = &D_object;

void f() {
  D_object.B::~B();             // calls B's destructor
  B_ptr->~B();                  // calls D's destructor
  B_ptr->~B_alias();            // calls D's destructor
  B_ptr->B_alias::~B();         // calls B's destructor
  B_ptr->B_alias::~B_alias();   // calls B's destructor
}

end example] [ Note: Явный вызов деструктора всегда должен быть написан с использованием member access оператора или qualified-id([expr.prim]); в частности, unary-expression ~X() функция-член не является явным вызовом деструктора ([expr.unary.op]). ]end note

[ Явные вызовы деструкторов нужны редко. Одно из применений таких вызовов - для объектов, размещенных по определенным адресам с помощью размещения . Такое использование явного размещения и уничтожения объектов может быть необходимо для работы с выделенными аппаратными ресурсами и для написания средств управления памятью. Например, Note: new-expression

void* operator new(std::size_t, void* p) { return p; }
struct X {
  X(int);
  ~X();
};
void f(X* p);

void g() {                      // rare, specialized use:
  char* buf = new char[sizeof(X)];
  X* p = new(buf) X(222);       // use buf[] and initialize
  f(p);
  p->X::~X();                   // cleanup
}

end note]

Как только деструктор вызывается для объекта, объект больше не существует; поведение не определено, если деструктор вызывается для объекта, работа которого lifetime завершилась. [ Example: Если деструктор для автоматического объекта вызывается явным образом, а блок впоследствии оставляется способом, который обычно вызывает неявное уничтожение объекта, поведение не определено. ]end example

[ Обозначение для явного вызова деструктора может использоваться для любого скалярного типа name ( ). Разрешение этого позволяет писать код без необходимости знать, существует ли деструктор для данного типа. Например: Note: [expr.pseudo]

typedef int I;
I* p;
p->I::~I();

end note]

15.5 Free store [class.free]

Любая функция распределения для класса T является статическим членом (даже если не объявлен явно static).

[Example:

class Arena;
struct B {
  void* operator new(std::size_t, Arena*);
};
struct D1 : B {
};

Arena*  ap;
void foo(int i) {
  new (ap) D1;      // calls B​::​operator new(std​::​size_­t, Arena*)
  new D1[i];        // calls ​::​operator new[](std​::​size_­t)
  new D1;           // ill-formed: ​::​operator new(std​::​size_­t) hidden
}

end example]

Когда объект удаляется с помощью delete-expressiona, функция освобождения ( для объектов, не являющихся массивами или для массивов) вызывается (неявно) для освобождения памяти, занятой объектом ( ).operator delete() operator delete[]() [basic.stc.dynamic.deallocation]

Поиск специфичной для класса функции освобождения памяти является частью общей функции поиска ([expr.delete]) и происходит следующим образом. Если delete-expression используется для освобождения объекта класса, статический тип которого имеет виртуальный деструктор, функция освобождения - это функция, выбранная в точке определения виртуального деструктора динамического типа ([class.dtor]).118 В противном случае, если delete-expression используется для освобождения объекта класса T или его массива, статический и динамический типы объекта должны быть идентичными, а имя функции освобождения ищется в области видимости T. Если этот поиск не может найти имя,[expr.delete]продолжается поиск общей функции освобождения памяти ( ). Если результат поиска неоднозначен или недоступен, или если поиск выбирает функцию освобождения размещения, программа имеет неправильный формат.

Любая функция освобождения класса X является статическим членом (даже если не объявлена ​​явно static). [Example:

class X {
  void operator delete(void*);
  void operator delete[](void*, std::size_t);
};

class Y {
  void operator delete(void*, std::size_t);
  void operator delete[](void*);
};

end example]

Поскольку функции распределения и освобождения членов static не могут быть виртуальными. [ Однако, когда of a относится к объекту типа класса, потому что фактически вызванная функция освобождения ищется в области действия класса, который является динамическим типом объекта, если деструктор является виртуальным, эффект тот же . Например, Note: cast-expressiondelete-expression

struct B {
  virtual ~B();
  void operator delete(void*, std::size_t);
};

struct D : B {
  void operator delete(void*);
};

void f() {
  B* bp = new D;
  delete bp;        // 1: uses D​::​operator delete(void*)
}

Здесь память для объекта класса, неD являющегося массивом, освобождается D​::​operator delete()из-за виртуального деструктора. ] [ Виртуальные деструкторы не влияют на фактически вызываемую функцию освобождения, когда a ссылается на массив объектов типа класса. Например, end noteNote: cast-expressiondelete-expression

struct B {
  virtual ~B();
  void operator delete[](void*, std::size_t);
};

struct D : B {
  void operator delete[](void*, std::size_t);
};

void f(int i) {
  D* dp = new D[i];
  delete [] dp;     // uses D​::​operator delete[](void*, std​::​size_­t)
  B* bp = new D[i];
  delete[] bp;      // undefined behavior
}

end note]

Доступ к функции освобождения проверяется статически. Следовательно, даже если на самом деле может быть выполнен другой, статически видимая функция освобождения должна быть доступна. [ Example: Для вызова в строке «// 1» выше, если бы B​::​operator delete() это было так private, выражение удаления было бы неправильно сформировано. ]end example

[ Note: Если функция освобождения не имеет явного значения noexcept-specifier, она не выбрасывает exception specification. ]end note

Подобное положение не требуется для версии массива, operator delete поскольку [expr.delete] в этой ситуации требуется, чтобы статический тип удаляемого объекта был таким же, как и его динамический тип.

15.6 Initialization [class.init]

Когда для объекта типа класса (или его массива) не()указан инициализатор или инициализатор имеет форму , объект инициализируется, как указано в [dcl.init].

Объект типа класса (или его массив) может быть инициализирован явно; видеть [class.expl.init] и [class.base.init].

Когда массив объектов класса инициализируется (явно или неявно) и элементы инициализируются конструктором, конструктор должен вызываться для каждого элемента массива, следуя порядку нижнего индекса; см [dcl.array]. [ Note: Деструкторы для элементов массива вызываются в порядке, обратном их построению. ]end note

15.6.1 Explicit initialization [class.expl.init]

Объект типа класса может быть инициализирован заключенным в круглые скобки expression-list, где expression-list конструируется как список аргументов для конструктора, который вызывается для инициализации объекта. В качестве альтернативы, single assignment-expression может быть указан как initializer using = форма инициализации. Применяется либо семантика прямой инициализации, либо семантика копирования-инициализации; см [dcl.init]. [Example:

struct complex {
  complex();
  complex(double);
  complex(double,double);
};

complex sqrt(complex,complex);

complex a(1);                   // initialize by a call of complex(double)
complex b = a;                  // initialize by a copy of a
complex c = complex(1,2);       // construct complex(1,2) using complex(double,double),
                                // copy/move it into c
complex d = sqrt(b,c);          // call sqrt(complex,complex) and copy/move the result into d
complex e;                      // initialize by a call of complex()
complex f = 3;                  // construct complex(3) using complex(double), copy/move it into f
complex g = { 1, 2 };           // initialize by a call of complex(double, double)

end example] [ не влияет на инициализацию. ]Note: Overloading of the assignment operatorend note

Объект типа класса также может быть инициализирован файлом braced-init-list. Применяется семантика инициализации списка; видеть [dcl.init] и [dcl.init.list]. [Example:

complex v[6] = { 1, complex(1,2), complex(), 2 };

Здесь complex​::​complex(double) вызывается для инициализации v[0] и v[3], complex​::​complex(​double, double) вызывается для инициализации v[1], complex​::​complex() вызывается для инициализации v[2], v[4]и v[5]. Другой пример:

struct X {
  int i;
  float f;
  complex c;
} x = { 99, 88.8, 77.7 };

Здесь x.i инициализируется 99, x.f 88,8 и complex​::​complex(double) вызывается для инициализации x.c. ] [ Фигурные скобки можно опустить в любом агрегате, даже если у агрегата есть члены типа класса с определяемыми пользователем преобразованиями типов; см . ]end exampleNote: initializer-list [dcl.init.aggr]end note

[ Note: Если T это тип класса без конструктора по умолчанию, любое объявление объекта типа T (или его массива) неправильно сформировано, если не initializer указано явно (см. [class.init] И [dcl.init]). ]end note

[ Порядок, в котором инициализируются объекты со статической продолжительностью хранения или хранением потоков, описан в и . ]Note: [basic.start.dynamic] [stmt.dcl]end note

15.6.2 Initializing bases and members [class.base.init]

В определении конструктора для класса инициализаторы для прямых и виртуальных подобъектов базового класса и нестатических членов данных могут быть указаны с помощью a ctor-initializer, который имеет форму

ctor-initializer:
	: mem-initializer-list
mem-initializer-list:
	mem-initializer ...opt
	mem-initializer-list , mem-initializer ...opt
mem-initializer:
	mem-initializer-id ( expression-listopt )
	mem-initializer-id braced-init-list
mem-initializer-id:
	class-or-decltype
	identifier

В mem-initializer-idисходном неквалифицированном поиске identifierвыполняется поиск в области действия класса конструктора, и, если он не найден в этой области, выполняется поиск в области, содержащей определение конструктора. [ Note: Если класс конструктора содержит член с тем же именем, что и прямой или виртуальный базовый класс класса, mem-initializer-id именование члена или базового класса, состоящее из одного идентификатора, относится к члену класса. Для mem-initializer-id скрытого базового класса можно указать полное имя. ] Если не названы класс конструктора, нестатический член данных класса конструктора или прямая или виртуальная база этого класса, объект имеет неправильный формат.end notemem-initializer-idmem-initializer

A mem-initializer-list может инициализировать базовый класс, используя любой, class-or-decltypeкоторый обозначает этот тип базового класса. [Example:

struct A { A(); };
typedef A global_A;
struct B { };
struct C: public A, public B { C(); };
C::C(): global_A() { }          // mem-initializer for base A

end example]

Если a mem-initializer-id неоднозначно, потому что он обозначает как прямой не виртуальный базовый класс, так и унаследованный виртуальный базовый класс, mem-initializer это неправильно сформировано. [Example:

struct A { A(); };
struct B: public virtual A { };
struct C: public A, public B { C(); };
C::C(): A() { }                 // ill-formed: which A?

end example]

A ctor-initializer может инициализировать вариантный член класса конструктора. Если a ctor-initializer указывает более одного mem-initializer для одного и того же члена или для одного и того же базового класса, ctor-initializer это неправильно сформировано.

A mem-initializer-listможет делегировать другому конструктору класса конструктора, используя любой, class-or-decltypeкоторый обозначает сам класс конструктора. Если mem-initializer-idобозначает класс конструктора, он должен быть единственным mem-initializer; конструктор - это delegating constructor, а конструктор, выбранный объектом, mem-initializer- это target constructor. Целевой конструктор выбирается по разрешению перегрузки. После возврата целевого конструктора выполняется тело делегирующего конструктора. Если конструктор прямо или косвенно делегирует себе полномочия, программа имеет неправильный формат и диагностика не требуется. [Example:

struct C {
  C( int ) { }                  // #1: non-delegating constructor
  C(): C(42) { }                // #2: delegates to #1
  C( char c ) : C(42.0) { }     // #3: ill-formed due to recursion with #4
  C( double d ) : C('a') { }    // #4: ill-formed due to recursion with #3
};

end example]

expression-list Или braced-init-list в mem-initializer используются для инициализации назначенного субобъекта (или, в случае делегирования конструктора, полный класс объекта) в соответствии с правилами инициализаций [dcl.init] для прямой инициализации. [Example:

struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };
struct D : B1, B2 {
  D(int);
  B1 b;
  const int c;
};

D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ }
D d(10);

end example] [ Note: Инициализация, выполняемая каждым из них, mem-initializer представляет собой файл full-expression. Любое выражение в a mem-initializer оценивается как часть полного выражения, выполняющего инициализацию. ] A, где означает, что виртуальный базовый класс игнорируется во время выполнения конструктора любого класса, который не является самым производным классом.end notemem-initializermem-initializer-id

Временное выражение, привязанное к ссылочному элементу в a, mem-initializer имеет неправильный формат. [Example:

struct A {
  A() : v(42) { }   // error
  const int& v;
};

end example]

В конструкторе без делегирования, если данный потенциально сконструированный подобъект не обозначен a mem-initializer-id (включая случай, когда его нет, mem-initializer-list потому что у конструктора нет ctor-initializer), тогда

  • если объект является нестатическим членом данных, который имеет default member initializer либо

    • класс конструктора - это union, и никакой другой вариантный член этого союза не обозначен символом mem-initializer-idили

    • класс конструктора не является объединением, и, если предприятие является членом анонимного союза, ни один из членов этого союза не обозначаются mem-initializer-id,

    сущность инициализируется из инициализатора члена по умолчанию, как указано в [dcl.init];

  • в противном случае, если сущность является анонимным объединением или вариантом member ([class.union.anon]), инициализация не выполняется;

  • в противном случае - сущность default-initialized.

[ Note: An abstract class никогда не является наиболее производным классом, поэтому его конструкторы никогда не инициализируют виртуальные базовые классы, поэтому соответствующий класс mem-initializers может быть опущен. ] Попытка инициализировать более одного нестатического элемента данных объединения приводит к неправильному формату программы. [ После того , как вызов конструктора для класса для объекта с автоматической или динамической продолжительностью хранения завершен, если конструктор не был вызван как часть значения инициализации и член не является ни инициализацией , ни присваивается значение во время выполнения из тело конструктора, член имеет неопределенное значение. ] [ end noteNote: XXcompound-statementend noteExample:

struct A {
  A();
};

struct B {
  B(int);
};

struct C {
  C() { }               // initializes members as follows:
  A a;                  // OK: calls A​::​A()
  const B b;            // error: B has no default constructor
  int i;                // OK: i has indeterminate value
  int j = 5;            // OK: j has the value 5
};

end example]

Если данный нестатический член данных имеет как инициализатор члена по умолчанию, так и a mem-initializer, выполняется инициализация, указанная в mem-initializer, и инициализатор члена нестатических данных по умолчанию игнорируется. [ Example: Учитывая

struct A {
  int i = /* some integer expression with side effects */ ;
  A(int arg) : i(arg) { }
  // ...
};

A(int) конструктор просто инициализации i со значением arg, и побочных эффектов в iинициализаторе члена по умолчанию «S не будет иметь место. ]end example

Временное выражение, привязанное к ссылочному элементу из инициализатора элемента по умолчанию, имеет неправильный формат. [Example:

struct A {
  A() = default;        // OK
  A(int v) : v(v) { }   // OK
  const int& v = 42;    // OK
};
A a1;                   // error: ill-formed binding of temporary to reference
A a2(1);                // OK, unfortunately

end example]

В конструкторе без делегирования деструктором для каждого потенциально сконструированного подобъекта типа класса является potentially invoked. [ Note: Это положение гарантирует, что деструкторы могут быть вызваны для полностью сконструированных подобъектов в случае возникновения исключения ([except.ctor]). ] end note

В конструкторе без делегирования инициализация выполняется в следующем порядке:

  • Во-первых, и только для конструктора most derived classвиртуальных базовых классов инициализируются в том порядке, в котором они появляются при обходе направленного ациклического графа базовых классов в глубину слева направо, где «слева направо» порядок появления базовых классов в производном классе base-specifier-list.

  • Затем прямые базовые классы инициализируются в порядке объявления, как они появляются в base-specifier-list (независимо от порядка mem-initializers).

  • Затем нестатические элементы данных инициализируются в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-initializers).

  • Наконец, compound-statementвыполняется тело конструктора.

[ Note: Порядок объявления необходим, чтобы гарантировать, что базовые и членские подобъекты уничтожаются в порядке, обратном инициализации. ]end note

[Example:

struct V {
  V();
  V(int);
};

struct A : virtual V {
  A();
  A(int);
};

struct B : virtual V {
  B();
  B(int);
};

struct C : A, B, virtual V {
  C();
  C(int);
};

A::A(int i) : V(i) { /* ... */ }
B::B(int i) { /* ... */ }
C::C(int i) { /* ... */ }

V v(1);             // use V(int)
A a(2);             // use V(int)
B b(3);             // use V()
C c(4);             // use V()

end example]

Имена в expression-list или braced-init-list из a mem-initializer оцениваются в области действия конструктора, для которого mem-initializer указан. [Example:

class X {
  int a;
  int b;
  int i;
  int j;
public:
  const int& r;
  X(int i): r(a), b(i), i(i), j(this->i) { }
};

инициализируется X​::​r для ссылки X​::​a, инициализируется X​::​b значением параметра конструктора i, инициализируется X​::​i значением параметра конструктора iи инициализируется X​::​j значением X​::​i; это происходит каждый раз, когда создается объект класса X . ] [ Поскольку они оцениваются в области видимости конструктора, указатель может использоваться в элементе a для ссылки на инициализируемый объект. ]end exampleNote: mem-initializerthisexpression-listmem-initializerend note

Для virtual member functionsстроящегося объекта можно вызывать функции-члены (в том числе ). Точно так же строящийся объект может быть операндом typeid operator или для dynamic_­cast. Однако, если эти операции выполняются в ctor-initializer (или в функции, вызываемой прямо или косвенно из a ctor-initializer) до завершения всех mem-initializers базовых классов for, программа имеет неопределенное поведение. [Example:

class A {
public:
  A(int);
};

class B : public A {
  int j;
public:
  int f();
  B() : A(f()),     // undefined: calls member function but base A not yet initialized
  j(f()) { }        // well-defined: bases are all initialized
};

class C {
public:
  C(int);
};

class D : public B, C {
  int i;
public:
  D() : C(f()),     // undefined: calls member function but base C not yet initialized
  i(f()) { }        // well-defined: bases are all initialized
};

end example]

[ Note: [class.cdtor] описывает результат вызовов виртуальных функций typeid и dynamic_­casts во время построения для четко определенных случаев; то есть описывает polymorphic behavior строящийся объект. ]end note

За mem-initializerкоторым следует многоточие pack expansion , инициализируются базовые классы, указанные в раскрытии пакета в base-specifier-list классе. [Example:

template<class... Mixins>
class X : public Mixins... {
public:
  X(const Mixins&... mixins) : Mixins(mixins)... { }
};

end example]

15.6.3 Initialization by inherited constructor [class.inhctor.init]

Когда конструктор для типа B вызывается для инициализации объекта другого типа D (то есть, когда конструктор был inherited), инициализация продолжается, как если бы конструктор по умолчанию использовался для инициализации D объекта и каждого подобъекта базового класса, от которого был унаследован конструктор. , за исключением того, что B подобъект инициализируется вызовом унаследованного конструктора. Полная инициализация считается одним вызовом функции; в частности, инициализация параметров унаследованного конструктора упорядочивается перед инициализацией любой части D объекта. [Example:

struct B1 {
  B1(int, ...) { }
};

struct B2 {
  B2(double) { }
};

int get();

struct D1 : B1 {
  using B1::B1;     // inherits B1(int, ...)
  int x;
  int y = get();
};

void test() {
  D1 d(2, 3, 4);    // OK: B1 is initialized by calling B1(2, 3, 4),
                    // then d.x is default-initialized (no initialization is performed),
                    // then d.y is initialized by calling get()
  D1 e;             // error: D1 has a deleted default constructor
}

struct D2 : B2 {
  using B2::B2;
  B1 b;
};

D2 f(1.0);          // error: B1 has a deleted default constructor

struct W { W(int); };
struct X : virtual W { using W::W; X() = delete; };
struct Y : X { using X::X; };
struct Z : Y, virtual W { using Y::Y; };
Z z(0);             // OK: initialization of Y does not invoke default constructor of X

template<class T> struct Log : T {
  using T::T;       // inherits all constructors from class T
  ~Log() { std::clog << "Destroying wrapper" << std::endl; }
};

Шаблон класса Log обертывает любой класс и пересылает все его конструкторы, одновременно записывая сообщение в стандартный журнал всякий раз, когда объект класса Log уничтожается. ]end example

Если конструктор был унаследован от нескольких подобъектов базового класса типа B, программа имеет неправильный формат. [Example:

struct A { A(int); };
struct B : A { using A::A; };

struct C1 : B { using B::B; };
struct C2 : B { using B::B; };

struct D1 : C1, C2 {
  using C1::C1;
  using C2::C2;
};

struct V1 : virtual B { using B::B; };
struct V2 : virtual B { using B::B; };

struct D2 : V1, V2 {
  using V1::V1;
  using V2::V2;
};

D1 d1(0);       // ill-formed: ambiguous
D2 d2(0);       // OK: initializes virtual B base class, which initializes the A base class
                // then initializes the V1 and V2 base classes as if by a defaulted default constructor

struct M { M(); M(int); };
struct N : M { using M::M; };
struct O : M {};
struct P : N, O { using N::N; using O::O; };
P p(0);             // OK: use M(0) to initialize N's base class,
                    // use M() to initialize O's base class

end example]

Когда объект инициализируется унаследованным конструктором, инициализация объекта завершается, когда завершается инициализация всех подобъектов.

15.7 Construction and destruction [class.cdtor]

Для объекта с нетривиальным конструктором обращение к любому нестатическому члену или базовому классу объекта до начала выполнения конструктора приводит к неопределенному поведению. Для объекта с нетривиальным деструктором обращение к любому нестатическому члену или базовому классу объекта после того, как деструктор завершает выполнение, приводит к неопределенному поведению. [Example:

struct X { int i; };
struct Y : X { Y(); };                  // non-trivial
struct A { int a; };
struct B : public A { int j; Y y; };    // non-trivial

extern B bobj;
B* pb = &bobj;                          // OK
int* p1 = &bobj.a;                      // undefined, refers to base class member
int* p2 = &bobj.y.i;                    // undefined, refers to member's member

A* pa = &bobj;                          // undefined, upcast to a base class type
B bobj;                                 // definition of bobj

extern X xobj;
int* p3 = &xobj.i;                      // OK, X is a trivial class
X xobj;

Другой пример:

struct W { int j; };
struct X : public virtual W { };
struct Y {
  int* p;
  X x;
  Y() : p(&x.j) {   // undefined, x is not yet constructed
    }
};

end example]

Чтобы явно или неявно преобразовать указатель (а glvalue) со ссылкой на объект класса X к указателю (ссылка) для прямого или косвенного базового класса B из X, строительства X и строительства всех своих прямых или косвенных оснований , которые прямо или косвенно derive from B должно начаться, и уничтожение этих классов не должно завершиться, в противном случае преобразование приведет к неопределенному поведению. Чтобы сформировать указатель на (или получить доступ к значению) прямого нестатического члена объекта obj, obj должно быть начато построение и его разрушение не должно завершаться, в противном случае вычисление значения указателя (или доступ к значению члена) приводит к неопределенному поведению. [Example:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };

struct E : C, D, X {
  E() : D(this),    // undefined: upcast from E* to A* might use path E*  D*  A*
                    // but D is not constructed

                    // “D((C*)this)” would be defined: E*  C* is defined because E() has started,
                    // and C*  A* is defined because C is fully constructed

  X(this) {}        // defined: upon construction of X, C/B/D/A sublattice is fully constructed
};

end example]

Функции-члены, в том числе virtual functions, могут вызываться во время построения или разрушения ([class.base.init]). Когда виртуальная функция вызывается прямо или косвенно из конструктора или из деструктора, в том числе во время создания или уничтожения нестатических элементов данных класса, и объект, к которому применяется вызов, является объектом (вызовите его x) в процессе создания или разрушения, вызываемая функция является последним переопределителем в классе конструктора или деструктора, а не переопределяющим его в более производном классе. Если при вызове виртуальной функции используется явное class member access выражение, а объектное выражение относится к полному объекту x или одному из подобъектов базового класса этого объекта, но не к x одному из его подобъектов базового класса, поведение не определено. [Example:

struct V {
  virtual void f();
  virtual void g();
};

struct A : virtual V {
  virtual void f();
};

struct B : virtual V {
  virtual void g();
  B(V*, A*);
};

struct D : A, B {
  virtual void f();
  virtual void g();
  D() : B((A*)this, this) { }
};

B::B(V* v, A* a) {
  f();              // calls V​::​f, not A​::​f
  g();              // calls B​::​g, not D​::​g
  v->g();           // v is base of B, the call is well-defined, calls B​::​g
  a->f();           // undefined behavior, a's type not a base of B
}

end example]

typeid operator Могут быть использованы в процессе строительства или уничтожения ([class.base.init]). Когда typeid используется в конструкторе (включая mem-initializerили default member initializer для нестатического члена данных) или в деструкторе, или используется в функции, вызываемой (прямо или косвенно) из конструктора или деструктора, если операнд typeid относится к строящемуся объекту или уничтожение, typeid дает std​::​type_­info объект, представляющий класс конструктора или деструктора. Если операнд typeid относится к строящемуся или разрушающемуся объекту, а статический тип операнда не является ни конструктором, ни классом деструктора, ни одной из его баз, поведение не определено.

dynamic_­casts может использоваться во время строительства или разрушения ([class.base.init]). Когда a dynamic_­cast используется в конструкторе (включая mem-initializerинициализатор члена или по умолчанию для нестатического члена данных) или в деструкторе, или используется в функции, вызываемой (прямо или косвенно) из конструктора или деструктора, если операнд элемента dynamic_­cast ссылается для строящегося или разрушаемого объекта этот объект считается наиболее производным объектом, имеющим тип класса конструктора или деструктора. Если операнд dynamic_­cast относится к объекту, находящемуся в процессе создания или уничтожения, а статический тип операнда не является указателем или объектом собственного класса конструктора или деструктора или одной из его баз, это dynamic_­cast приводит к неопределенному поведению. [Example:

struct V {
  virtual void f();
};

struct A : virtual V { };

struct B : virtual V {
  B(V*, A*);
};

struct D : A, B {
  D() : B((A*)this, this) { }
};

B::B(V* v, A* a) {
  typeid(*this);        // type_­info for B
  typeid(*v);           // well-defined: *v has type V, a base of B yields type_­info for B
  typeid(*a);           // undefined behavior: type A not a base of B
  dynamic_cast<B*>(v);  // well-defined: v of type V*, V base of B results in B*
  dynamic_cast<B*>(a);  // undefined behavior, a has type A*, A not a base of B
}

end example]

15.8 Copying and moving class objects [class.copy]

Объект класса можно скопировать или переместить двумя способами: путем инициализации ([class.ctor], [dcl.init]), в том числе для передачи аргументов функции ([expr.call]) и для function value return; и по assignment. Концептуально эти две операции реализуются конструктором копирования / перемещения ([class.ctor]) и оператором присваивания копирования / перемещения ([over.ass]).

Программа неправильно сформирована, если конструктор копирования / перемещения или оператор присваивания копирования / перемещения для объекта неявно используется odr, а специальная функция-член - нет accessible. [ Note: Копирование / перемещение одного объекта в другой с помощью конструктора копирования / перемещения или оператора присваивания копирования / перемещения не изменяет макет или размер любого объекта. ]end note

15.8.1 Copy/move constructors [class.copy.ctor]

Конструктор без шаблона для класса X является ,copy constructor если ее первый параметр имеет тип X&, const X&, volatile X& или const volatile X&, и либо нет других параметров или также все другие параметры default arguments. [ Example: X​::​X(const X&) и X​::​X(X&,int=1) являются конструкторами копирования.

struct X {
  X(int);
  X(const X&, int = 1);
};
X a(1);             // calls X(int);
X b(a, 0);          // calls X(const X&, int);
X c = b;            // calls X(const X&, int);

end example]

Конструктор без шаблона для класса X является , move constructor если ее первый параметр имеет тип X&&, const X&&, volatile X&&или const volatile X&&, и либо нет других параметров или также все другие параметры default arguments. [ Example: Y​::​Y(Y&&) - конструктор перемещения.

struct Y {
  Y(const Y&);
  Y(Y&&);
};
extern Y f(int);
Y d(f(1));          // calls Y(Y&&)
Y e = d;            // calls Y(const Y&)

end example]

[ Note: Все формы конструктора копирования / перемещения могут быть объявлены для класса. [Example:

struct X {
  X(const X&);
  X(X&);            // OK
  X(X&&);
  X(const X&&);     // OK, but possibly not sensible
};

end example] ]end note

[ Note: Если класс X имеет только конструктор копирования с параметром типа X&, инициализатор типа const X или volatile X не может инициализировать объект типа (возможно, с квалификацией cv) X. [Example:

struct X {
  X();              // default constructor
  X(X&);            // copy constructor with a non-const parameter
};
const X cx;
X x = cx;           // error: X​::​X(X&) cannot copy cx into x

end example] ]end note

Объявление конструктора для класса X неправильно сформировано, если его первый параметр имеет тип (необязательно cv-квалифицированный) X и либо нет других параметров, либо все другие параметры имеют аргументы по умолчанию. Шаблон функции-члена никогда не создается для создания такой сигнатуры конструктора. [Example:

struct S {
  template<typename T> S(T);
  S();
};

S g;

void h() {
  S a(g);           // does not instantiate the member template to produce S​::​S<S>(S);
                    // uses the implicitly declared copy constructor
}

end example]

Если в определении класса явно не объявляется конструктор копирования, объявляется неявный конструктор implicitly. Если в определении класса объявляется конструктор перемещения или оператор присваивания перемещения, неявно объявленный конструктор копирования определяется как удаленный; в противном случае он определяется как defaulted. Последний случай считается устаревшим, если в классе есть объявленный пользователем оператор присваивания копии или объявленный пользователем деструктор.

Неявно объявленный конструктор копирования для класса X будет иметь форму

X::X(const X&)

если каждый потенциально сконструированный подобъект типа класса M (или его массива) имеет конструктор копирования, первый параметр которого имеет тип const M& или const volatile M&.119 В противном случае неявно объявленный конструктор копии будет иметь вид

X::X(X&)

Если в определении класса X явно не объявляется конструктор перемещения, неявный конструктор будет неявно объявлен как заданный по умолчанию тогда и только тогда, когда

  • X не имеет объявленного пользователем конструктора копирования,

  • X не имеет объявленного пользователем оператора присваивания копии,

  • X не имеет объявленного пользователем оператора присваивания перемещения, и

  • X не имеет деструктора, объявленного пользователем.

[ Note: Когда конструктор перемещения не объявляется неявно или не предоставляется явно, выражения, которые в противном случае вызвали бы конструктор перемещения, могут вместо этого вызывать конструктор копирования. ] end note

Неявно объявленный конструктор перемещения для класса X будет иметь вид

X::X(X&&)

Неявно объявленный конструктор копирования / перемещения является inline public членом своего класса. Конструктор копирования / перемещения по умолчанию для класса X определяется так, как deleted если бы он X имел:

  • вариантный член с нетривиальным соответствующим конструктором и X является классом, подобным объединению,

  • потенциально сконструированный тип подобъекта M (или его массив), который не может быть скопирован / перемещен, поскольку overload resolutionв применении к Mсоответствующему конструктору find приводит к неоднозначности или к функции, которая удаляется или недоступна из конструктора по умолчанию,

  • любой потенциально сконструированный подобъект типа с деструктором, который удален или недоступен из конструктора по умолчанию, или

  • для конструктора копирования - нестатический член данных ссылочного типа rvalue.

Конструктор перемещения по умолчанию, который определен как удаленный, игнорируется разрешением перегрузки ([over.match], [over.over]). [ Note: В противном случае удаленный конструктор перемещения помешал бы инициализации из rvalue, который вместо этого может использовать конструктор копирования. ]end note

Конструктор копирования / перемещения для класса X является тривиальным, если он не предоставляется пользователем и если:

  • класс X не имеет virtual functions и нет virtual base classes, и

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

  • для каждого нестатического члена данных, X который относится к типу класса (или его массиву), конструктор, выбранный для копирования / перемещения этого члена, является тривиальным;

в противном случае - конструктор копирования / перемещения non-trivial.

Конструктор копирования / перемещения, который установлен по умолчанию и не определен как удаленный, - implicitly defined это если он есть odr-used или когда он явно задан по умолчанию после его первого объявления. [ Note: Конструктор копирования / перемещения определяется неявно, даже если реализация опускает его odr-use ([class.temporary]). ] Если неявно определенный конструктор удовлетворяет требованиям a , то неявно определенный конструктор будет .end noteconstexpr constructor constexpr

Прежде чем конструктор копирования / перемещения по умолчанию для класса будет неявно определен, все не предоставленные пользователем конструкторы копирования / перемещения для его потенциально созданных подобъектов должны быть неявно определены. [ Note: Неявно объявленный конструктор копирования / перемещения имеет подразумеваемый exception specification. ]end note

Неявно определенный конструктор копирования / перемещения для класса, неX являющегося объединением, выполняет поэлементное копирование / перемещение его баз и членов. [ Note: Инициализаторы нестатических элементов данных по умолчанию игнорируются. См. Также пример в [class.base.init]. ] Порядок инициализации такой же, как порядок инициализации баз и членов в определяемом пользователем конструкторе (см. ). Пусть будет либо параметром конструктора, либо, для конструктора перемещения, значением xvalue, относящимся к параметру. Каждый базовый или нестатический член данных копируется / перемещается в соответствии с его типом: end note [class.base.init] x

  • если член является массивом, каждый элемент напрямую инициализируется соответствующим подобъектом x;

  • если член m имеет ссылочный тип rvalue T&&, он инициализируется напрямую с помощью static_­cast<T&&>(x.m);

  • в противном случае база или член инициализируются напрямую соответствующей базой или членом x.

Подобъекты виртуального базового класса должны быть инициализированы только один раз неявно определенным конструктором копирования / перемещения (см [class.base.init]. Раздел "Ресурсы" ).

Неявно определенный конструктор копирования / перемещения для союзной X копирует object representation из X.

Это означает, что ссылочный параметр неявно объявленного конструктора копирования не может связываться с volatile lvalue; см [diff.special].

15.8.2 Copy/move assignment operator [class.copy.assign]

Пользователь объявленной copy оператор присваивания X​::​operator= не является членом статической функцией не-шаблона класса X ровно с одним параметром типа X, X&, const X&, volatile X& или const volatile X&.120 [ Note: Должен быть объявлен перегруженный оператор присваивания, имеющий только один параметр; см [over.ass]. ] [ Для класса может быть объявлено более одной формы оператора присваивания копии. ] [ Если у класса есть только оператор присваивания копии с параметром типа , выражение типа const не может быть присвоено объекту типа . [end noteNote: end noteNote: XX&XXExample:

struct X {
  X();
  X& operator=(X&);
};
const X cx;
X x;
void f() {
  x = cx;           // error: X​::​operator=(X&) cannot assign cx into x
}

end example] ]end note

Если в определении класса явно не объявляется оператор присваивания копии, он объявляется implicitly. Если определение класса объявляет конструктор перемещения или оператор присваивания перемещения, неявно объявленный оператор присваивания копии определяется как удаленный; в противном случае он определяется как defaulted. Последний случай считается устаревшим, если в классе есть объявленный пользователем конструктор копии или объявленный пользователем деструктор. Неявно объявленный оператор присваивания копии для класса X будет иметь вид

X& X::operator=(const X&)

если

  • каждый непосредственный базовый класс B из X имеет оператор присваивания , копирование параметры которого имеет типа const B&, const volatile B& или B, и

  • для всех нестатических членов данных, X которые относятся к типу класса M (или его массиву), каждый такой тип класса имеет оператор присваивания копии, параметр которого имеет тип const M&, const volatile M& или M.121

В противном случае неявно объявленный оператор присваивания копии будет иметь вид

X& X::operator=(X&)

Пользователь объявленной move оператор присваивания X​::​operator= не является членом статической функцией не-шаблона класса X ровно с одним параметром типа X&&, const X&&, volatile X&&, или const volatile X&&. [ Note: Должен быть объявлен перегруженный оператор присваивания, имеющий только один параметр; см [over.ass]. ] [ Для класса может быть объявлено более одной формы оператора присваивания перемещения. ] end noteNote: end note

Если в определении класса X явно не объявляется оператор присваивания перемещения, он будет неявно объявлен как принятый по умолчанию тогда и только тогда, когда

  • X не имеет объявленного пользователем конструктора копирования,

  • X не имеет конструктора перемещения, объявленного пользователем,

  • X не имеет объявленного пользователем оператора присваивания копии, и

  • X не имеет деструктора, объявленного пользователем.

[ Example: Определение класса

struct S {
  int a;
  S& operator=(const S&) = default;
};

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

struct S {
  int a;
  S& operator=(const S&) = default;
  S& operator=(S&&) = default;
};

end example]

Неявно объявленный оператор присваивания перемещения для класса X будет иметь вид

X& X::operator=(X&&);

Неявно объявленный оператор присваивания копирования / перемещения для класса X имеет тип возвращаемого значения X&; он возвращает объект, для которого вызывается оператор присваивания, то есть объект, которому назначен. Неявно объявленный оператор присваивания копирования / перемещения является inline public членом своего класса.

Оператор присваивания копирования / перемещения по умолчанию для класса X определяется как удаленный, если X он:

  • вариантный член с нетривиальным соответствующим оператором присваивания и X является классом, подобным объединению, или

  • нестатический член данных const неклассового типа (или его массив), или

  • нестатический член данных ссылочного типа, или

  • прямой нестатический член данных типа класса M (или его массива) или прямой базовый класс, M который не может быть скопирован / перемещен, поскольку overload resolutionв применении к Mсоответствующему оператору присваивания find приводит к неоднозначности или к функции, которая удаляется или недоступна из оператора присваивания по умолчанию.

Оператор присваивания перемещения по умолчанию, который определен как удаленный, игнорируется разрешением перегрузки ([over.match], [over.over]).

Поскольку оператор присваивания копирования / перемещения неявно объявляется для класса, если он не объявлен пользователем, оператор присваивания копирования / перемещения базового класса всегда скрывается соответствующим оператором присваивания производного класса ([over.ass]). A, using-declarationкоторый переносит из базового класса оператор присваивания с типом параметра, который может быть типом оператора присваивания копирования / перемещения для производного класса, не считается явным объявлением такого оператора и не подавляет неявное объявление производного оператор класса; оператор, представленный с помощью using-declaration , скрыт неявно объявленным оператором в производном классе.

Оператор присваивания копирования / перемещения для класса X является тривиальным, если он не предоставляется пользователем и если:

  • класс X не имеет virtual functions и нет virtual base classes, и

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

  • для каждого нестатического элемента данных X , принадлежащего к типу класса (или его массиву), оператор присваивания, выбранный для копирования / перемещения этого члена, является тривиальным;

в противном случае используется оператор присваивания копирования / перемещения non-trivial.

Оператор присваивания копирования / перемещения для класса, X который установлен по умолчанию и не определен как удаленный, - implicitly defined это когда он есть odr-used (например, когда он выбран разрешением перегрузки для назначения объекту своего типа класса) или когда он явно используется по умолчанию после его первого декларация. Неявно определенный оператор присваивания копирования / перемещения - это constexpr если

  • X это буквальный тип, а

  • оператор присваивания, выбранный для копирования / перемещения каждого прямого подобъекта базового класса, является функцией constexpr, и

  • для каждого нестатического члена данных, X имеющего тип класса (или его массив), оператор присваивания, выбранный для копирования / перемещения этого члена, является функцией constexpr.

Перед тем как оператор присваивания копирования / перемещения по умолчанию для класса будет неявно определен, все операторы присваивания копирования / перемещения, не предоставляемые пользователем, для его прямых базовых классов и его нестатических элементов данных должны быть неявно определены. [ Note: Неявно объявленный оператор присваивания копирования / перемещения имеет подразумеваемый exception specification. ]end note

Неявно определенный оператор присваивания копирования / перемещения для класса, не являющегося объединением, X выполняет поэлементное присвоение его подобъектов копированию / перемещению. Первыми X назначаются прямые базовые классы в порядке их объявления в base-specifier-list, а затемX назначаются непосредственные нестатические члены данных в том порядке, в котором они были объявлены в определении класса. Пусть x будет либо параметром функции, либо, для оператора перемещения, значением x, относящимся к параметру. Каждому подобъекту присваивается способ, соответствующий его типу:

  • если подобъект имеет тип класса, как если бы при вызове operator= с подобъектом в качестве выражения объекта и соответствующим подобъектом x в качестве единственного аргумента функции (как если бы посредством явной квалификации; то есть игнорирование любых возможных виртуальных замещающих функций в более производных классы);

  • если подобъект является массивом, каждому элементу присваивается способ, соответствующий типу элемента;

  • если подобъект скалярного типа, используется встроенный оператор присваивания.

Не указано, назначаются ли подобъекты, представляющие виртуальные базовые классы, более одного раза неявно определенным оператором присваивания копирования / перемещения. [Example:

struct V { };
struct A : virtual V { };
struct B : virtual V { };
struct C : B, A { };

Не указано,V назначается ли подобъект виртуального базового класса дважды неявно определенным оператором присваивания копирования / перемещения для C. ]end example

Неявно определенный оператор копирующего присваивания для союзной X копирует object representation из X.

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

Это означает, что ссылочный параметр неявно объявленного оператора присваивания копии не может связываться с volatile lvalue; см [diff.special].

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]

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