Путаница при инициализации копии и прямой инициализации

Рассмотрим простую инструкцию (взято из Есть ли разница в С++ между инициализацией копирования и прямой инициализацией?:

A c2 = A();

Значение этого оператора инициализирует временные, а затем копии, которые значение в c2 (см. 5.2.3/2 и 8.5/14). Это, конечно, потребует неявный конструктор копирования (см. 8.5/14 и 12.3.1/3 и 13.3.1.3/1)

[Обратите внимание на смелое предложение в параграфе] → Мой вопрос: почему?

Теперь рассмотрим этот код:

class B {};
struct A 
{
  A(B const&) {}
  A(A const&) = delete;
  //A(A const&); //delete above statement and uncomment this statement, 
  //and everything works, even though there in no body of copy constructor Oo
};

A a2 = B();    //error since there is no copy constructor oO

Почему для копирования-инициализации требуется наличие конструктора копирования, хотя он не нужен когда-то, как представлено в приведенном выше коде

Пожалуйста, пожалуйста, еще одна вещь:

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

[ Обратите внимание на жирный шрифт в следующем параграфе]

Не означает ли это, что прямая инициализация имеет доступ ко всем конструкторам и может выполнять неявную последовательность преобразований, в то время как все инициализация копирования может выполнять только неявная последовательность преобразований?, Я хочу спросить, неявное преобразование в прямой инициализации отличается от неявной последовательности преобразований при инициализации копии?

Ответ 1

Правила оценки

A a1 = B();   // (1) copy-initialization
A a2 = {B()}; // (2) copy-initialization
A a3{B()};    // (3) direct-initialization

исходит из [dcl.init]/17:

- Если инициализатор представляет собой (не заключенный в скобки) бит-init-list, объект или ссылка инициализируется списком (8.5.4).
- [...]
- Если тип назначения - тип класса (возможно, cv-qualit):

  • Если инициализация является прямой инициализацией или если она является копией-инициализацией, где cv-unqualified версия типа источника - это тот же класс, что и производный класс класса, конструкторы. [...]
  • В противном случае (то есть для остальных экземпляров копирования-инициализации) пользовательские последовательности преобразования который может преобразовать из типа источника в тип назначения или (когда функция преобразования ) к его производному классу перечислены, как описано в 13.3.1.4, и лучший из них выбирается с помощью разрешения перегрузки (13.3). [...] Результат вызова (который является временным для случая конструктора) затем используется для direct-initialize, в соответствии с вышеприведенными правилами, объект, который является местом назначения инициализации копирования. В некоторых случаях реализация разрешено исключать копирование, присущее этой прямой инициализации, путем создания промежуточный результат непосредственно в инициализированный объект; см. 12.2, 12.8.

Как для a2, так и для a3, инициализатор представляет собой бит-init-list, поэтому мы просто выполняем инициализацию списка. Это заканчивается вызовом конструктора B const&.

Для a1 первая подпула не применяется - поскольку тип источника (B) не является тем же или производным классом целевого типа (A). Таким образом, мы переходим во вторую точку подпункта, которая включает в себя рассмотрение функций преобразования. Существует один (A(B const&)), поэтому мы эффективно переписываем выражение

A a1_new{A{B{}}};

Теперь, как правило, эта дополнительная копия будет удалена. Но вы явно запрещаете это, поэтому код не может скомпилироваться.


Что касается почему дифференцирования? Я не знаю. Кажется, что копирование-инициализация должна быть просто синтаксическим сахаром для прямой инициализации. В большинстве случаев, в конце концов, это...