Здесь действуют несколько сил. Чтобы понять, что происходит, давайте посмотрим, куда нас приведет (Foo)x
. Прежде всего, это приведение в стиле c эквивалентно static_cast
в данном конкретном случае. И семантика статического приведения будет заключаться в прямой инициализации объекта результата. Поскольку объект результата будет иметь тип класса, [dcl.init] /17.6.2 говорит нам, что он инициализирован следующим образом:
В противном случае, если инициализация является прямой инициализацией, или если это
инициализация копии, где cv-неквалифицированная версия источника
тип тот же класс, или производный класс, класса
назначения, конструкторы считаются. Применимые конструкторы
перечисляются ([over.match.ctor]), а лучший выбирается через
разрешение перегрузки. Выбранный конструктор называется
инициализировать объект с помощью выражения инициализатора или
список выражений в качестве аргумента (ов). Если конструктор не применяется, или
Разрешение перегрузки неоднозначно, инициализация некорректна.
Таким образом, разрешение перегрузки, чтобы выбрать конструктор Foo
для вызова. А если не удается разрешить перегрузку, программа работает некорректно. В этом случае он не должен завершиться ошибкой, даже если у нас есть 3 возможных конструктора. Это Foo(int)
, Foo(Foo const&)
и Foo(Foo&&)
.
Для начала нам нужно скопировать инициализировать int
в качестве аргумента в конструктор, а это значит найти неявную последовательность преобразования из Bar<char>
в int
. Поскольку пользовательский оператор преобразования, указанный вами с Bar<char>
в char
, не является явным, мы можем использовать его для неявной последовательности разговора Bar<char> -> char -> int
.
Для двух других конструкторов нам нужно привязать ссылку к Foo
. Однако мы не можем этого сделать. Согласно [over.match.ref] / 1 :
При условиях, указанных в [dcl.init.ref], ссылка может быть
связаны непосредственно с glvalue или классом prvalue, который является результатом
применение функции преобразования к выражению инициализатора. перегрузка
Разрешение используется для выбора функции преобразования, которая будет вызвана.
Предполагая, что «cv1 T» является базовым типом ссылки, являющейся
инициализировано, и «cv S» является типом выражения инициализатора,
с S типом класса функции-кандидаты выбираются следующим образом:
- Рассматриваются функции преобразования S и его базовых классов. Те неявные функции преобразования, которые не скрыты в S
и выдайте тип «lvalue ссылка на cv2 T2» (при инициализации
lvalue ссылка или rvalue ссылка на функцию) или «cv2 T2» или
«Rvalue ссылка на cv2 T2» (при инициализации rvalue ссылки или
lvalue ссылка на функцию), где «cv1 T»
совместимый с ссылками ([dcl.init.ref]) с «cv2 T2», является кандидатом
функции. Для прямой инициализации, эти явные преобразования
функции, которые не скрыты в S и дают тип «lvalue»
ссылка на cv2 T2 »или« cv2 T2 »или« rvalue ссылка на cv2 T2 »
соответственно, где T2 имеет тот же тип, что и T, или может быть преобразован в
Тип T с квалификационным преобразованием ([conv.qual]), также
функции-кандидаты.
Единственная функция преобразования, которая может дать нам glvalue или prvalue типа Foo
, - это специализация указанного вами явного шаблона функции преобразования. Но, поскольку инициализация аргументов функции не является прямой инициализацией, мы не можем рассмотреть явную функцию преобразования. Поэтому мы не можем вызывать копирование или перемещать конструкторы в разрешении перегрузки. Это оставляет нас только с конструктором, принимающим int
. Таким образом, разрешение перегрузки является успешным, и это должно быть так.
Тогда почему некоторые компиляторы находят егонеоднозначно, или вместо этого вызывать оператор шаблонного преобразования? Что ж, поскольку в стандарт было введено гарантированное разрешение копирования, было отмечено ( CWG выпуск 2327 ), что определяемые пользователем функции преобразования также должны способствовать разрешению копирования. Сегодня, согласно сухому письму стандарта, они этого не делают. Но мы бы очень хотели, чтобы они. Хотя формулировка того, как именно это должно быть сделано, все еще разрабатывается, может показаться, что некоторые компиляторы уже пытаются это реализовать.
И это та реализация, которую вы видите. Это противодействующая сила расширения разрешения копирования, которая мешает разрешению перегрузки здесь.