Ответ 1
Если второй конструктор закомментирован, то S {{}} остается действительным выражением, но (я уверен) в этом случае вызывается конструктор move из построенного по умолчанию экземпляра S {}.
Собственно, это не то, что происходит. Порядок в [dcl.init.list]:
Список-инициализация объекта или ссылки типа T определяется следующим образом:
- Если T является совокупным классом, а в списке инициализаций имеется один элемент типа cv U, [...]
- В противном случае, если T - массив символов и [...]
- В противном случае, если T является агрегатом, выполняется агрегатная инициализация (8.6.1).
Как только вы удаляете конструктор S(void *)
, S
становится агрегатом - он не имеет конструктора, предоставленного пользователем. S() = default
не считается предоставленным пользователем, потому что причины. Агрегатная инициализация из {}
закончится инициализацией значения i
.
Почему конструктор преобразования имеет приоритет над значением по умолчанию в самом первом случае?
Оставаясь void*
, продолжайте движение вниз по списку маркеров:
- В противном случае, если в списке инициализаторов нет элементов [...]
- В противном случае, если T является специализацией std:: initializer_list, [...]
- В противном случае, если T - тип класса, рассматриваются конструкторы. Соответствующие конструкторы перечислены и лучший выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7).
[over.match.list] дает нам двухфазный процесс разрешения перегрузки:
- Изначально функции-кандидаты являются конструкторами-списками инициализаторов (8.6.4) класса T и Список аргументов состоит из списка инициализаторов как одного аргумента.
- Если не найдено никакого жизнеспособного конструктора списка инициализаторов, снова выполняется разрешение перегрузки, где Функции-кандидаты - все конструкторы класса T, а список аргументов состоит из элементов списка инициализаторов.Если в списке инициализаторов нет элементов, а T имеет конструктор по умолчанию, первая фаза будет опущена.
S
не имеет конструкторов списка инициализаторов, поэтому мы переходим ко второй пуле и перечисляем все конструкторы с списком аргументов {}
. У нас есть несколько жизнеспособных конструкторов:
S(S const& );
S(S&& );
S(void *);
Последовательности преобразования определены в [over.ics.list]:
В противном случае, если параметр является неагрегатным классом X и разрешением перегрузки на 13.3.1.7, выбирается один лучший конструктор C из X для выполнения инициализации объекта типа X из списка инициализатора аргументов:
- Если C не является конструктором списка инициализаторов, а в списке инициализаций имеется один элемент типа cv U, [...] - В противном случае, неявная последовательность преобразования представляет собой пользовательскую последовательность преобразований со второй стандартной последовательностью преобразования с преобразованием идентичности.
и
В противном случае, если тип параметра не является классом: [...] - если в списке инициализаторов нет элементов, неявная последовательность преобразований - это преобразование идентичности.
Таким образом, конструкторы S(S&& )
и S(S const& )
являются как пользовательскими последовательностями преобразования, так и преобразованием идентичности. Но S(void *)
- это просто преобразование идентичности.
Но, [over.best.ics] имеет это дополнительное правило:
Однако, если цель - - первый параметр конструктора или
- параметр неявного объекта определяемой пользователем функции преобразования
и конструктор или пользовательская функция преобразования - это кандидат от
- 13.3.1.3, когда [...]
- 13.3.1.4, 13.3.1.5 или 13.3.1.6 (во всех случаях), или
- вторая фаза 13.3.1.7, когда список инициализаторов имеет ровно один элемент, который сам является списком инициализаторов, а целью является первый параметр конструктора классаX
, а преобразование -X
или ссылку на (возможно, cv-квалификацию)X
,пользовательские последовательности преобразования не рассматриваются.
Это исключает из рассмотрения S(S const&)
и S(S&& )
в качестве кандидатов - именно в этом случае - целью является первый параметр конструктора в результате второй фазы [over.match.list] и целевой как ссылка на возможную cv-квалификацию S
, и такая последовательность преобразования будет определяться пользователем.
Следовательно, единственным оставшимся кандидатом является S(void *)
, поэтому он тривиально является лучшим жизнеспособным кандидатом.