Ответ 1
Как отметил Никол Болас, исходная версия этого ответа была неправильной: cppreference на момент написания неправильно документировал порядок, в котором конструкторы рассматривались в инициализации списка. Ниже приведен ответ с использованием правил, поскольку они существуют в черновом проекте стандарта n4140, который очень близок к официальному стандарту С++ 14.
Текст оригинального ответа по-прежнему включен для записи.
Обновленный ответ
В комментарии Пер Натана Оливера gcc и clang производят разные результаты в этой ситуации:
g++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor
initializer list
clang++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor
gcc верен.
n4140 [dcl.init.list]/1
Инициализация списка - это инициализация объекта или ссылки из списка с привязкой к init.
Вы используете там инициализацию списка, а так как c
- объект, правила его инициализации списка определены в [dcl.init.list]/3:
[Dcl.init.list]/3:
Инициализация списка объекта или ссылки типа T определяется следующим образом:
- Если
T
является совокупностью...- В противном случае, если в списке инициализаторов нет элементов...
- В противном случае, если
T
является специализациейstd::initializer_list<E>
...
просматривая список до сих пор:
-
Foo
не является совокупностью. - Он имеет один элемент.
-
Foo
не является специализациейstd::initializer_list<E>
.
Затем мы нажмем [dcl.init.list]/3.4:
В противном случае, если
T
- тип класса, рассматриваются конструкторы. Применяемые конструкторы перечисляются, а лучший выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). Если для преобразования любого из аргументов требуется сужение преобразования (см. Ниже), программа плохо сформирована.
Теперь мы куда-то попадаем. 13.3.1.7 также известен как [over.match.list]:
Инициализация с помощью инициализации списка
Когда объекты неагрегатного типа типаT
инициализируются списком (8.5.4), разрешение перегрузки выбирает конструктор в две фазы:
- Первоначально функции-кандидаты являются конструкторами-инициализаторами-списками (8.5.4) класса
T
а список аргументов состоит из списка инициализаторов как одного аргумента.- Если не найден жизнеспособный конструктор списка инициализаторов, разрешение перегрузки выполняется снова, где функции-кандидаты являются всеми конструкторами класса
T
а список аргументов состоит из элементов списка инициализаторов.
Таким образом, конструктор копирования будет учитываться только после конструкторов списка инициализаторов во второй фазе разрешения перегрузки. Здесь должен использоваться конструктор списка инициализаторов.
Стоит отметить, что [over.match.list] затем продолжается:
Если в списке инициализаторов нет элементов, а
T
имеет конструктор по умолчанию, первая фаза будет опущена. При инициализации списка копий, если выбран явный конструктор, инициализация плохо сформирована.
и что после [dcl.init.list]/3. 5 относится к одноэлементной инициализации списка:
В противном случае, если в списке инициализаторов есть один элемент типа
E
и либоT
не является ссылочным типом, либо его ссылочный тип связан с ссылкой наE
, объект или ссылка инициализируются из этого элемента; если для преобразования элемента вT
требуется преобразование сужения (см. ниже), программа плохо сформирована.
в котором объясняется, где cppreference получил свой специальный случай для инициализации одноэлементного списка, хотя они поместили его выше в порядке, чем это должно быть.
Оригинальный ответ
Вы сталкиваетесь с интересным аспектом инициализации списка, где, если список выполняет определенные требования, он может рассматриваться как инициализация копирования, а не инициализация списка.
из cppreference:
Эффекты инициализации списка объекта типа
T
:Если
T
- тип класса, а в списке инициализаторов есть один элемент того же или производного типа (возможно, cv-квалифицированный), объект инициализируется из этого элемента (путем инициализации копирования для инициализации списка копий или путем прямого преобразования, инициализация для инициализации прямого списка). (поскольку С++ 14)
Foo c{b}
выполняет все эти требования.