Ответ 1
TL;DR; Код хорошо сформирован. Условное выражение будет иметь тип int
и значение 2
. Это ошибка MSVC.
Из [expr.cond]:
В противном случае, если второй и третий операнды имеют разные типы и имеют (возможно, cv-qualified) тип класса или [...], делается попытка сформировать неявную последовательность конверсий (13.3.3.1) из каждого из этих операндов к типу другого. [...] Делаются попытки создать неявную последовательность преобразований из выражения операнда
E1
типаT1
в целевой тип, относящийся к типуT2
выражения операндаE2
следующим образом: [...]
- Если E2 является lvalue, [...]
- Если E2 является значением x, [...]
- Если E2 является prvalue или если ни одна из преобразованных последовательностей выше не может быть сформирована и по меньшей мере одна из операнды имеют (возможно, cv-qualified) тип класса:
- если T1 и T2 являются одним и тем же типом класса (игнорируя cv-квалификацию) и T2, по меньшей мере, cv-квалифицируется как T1, целевой тип T2,
- в противном случае целевой тип - это тип, который E2 имел бы после применения lvalue-to-rvalue (4.1), (4.2) и стандартное преобразование по функциям (4.1).
Итак, мы пытаемся сформировать неявную последовательность преобразований из типа std::integral_constant<int, 1>
, чтобы напечатать std::integral_constant<int, 2>
. Это нежизнеспособно. Не существует также и неявной последовательности преобразования в обратном направлении. Эти типы просто не взаимозаменяемы.
Итак, мы продолжаем:
Если преобразование отсутствует последовательность может быть сформирована, операнды остаются неизмененными и дальнейшая проверка выполняется, как описано ниже. [...]
Если второй и третий операнды являются glvalues одной и той же категории значений и имеют один и тот же тип, [...]
В противном случае результатом будет prvalue. Если второй и третий операнды не имеют одного и того же типа, и имеет (возможно, cv-qualified) тип класса, разрешение перегрузки используется для определения конверсий (если есть) применяется к операндам (13.3.1.2, 13.6). Если разрешение перегрузки выходит из строя, программа плохо сформирована.
Хорошо, какое разрешение перегрузки мы можем выполнить? Из [over.match.oper]:
Если какой-либо из операндов имеет тип, который является классом или перечислением, может быть объявлена определяемая пользователем операторная функция, которая реализует этот оператор или может быть необходимо преобразование, определяемое пользователем, чтобы преобразовать операнд в тип, подходящий для встроенного оператора.
Если встроенные функции указаны в [over.built] как:
Для каждой пары продвинутых арифметических типов L и R существуют кандидатные операторные функции вида
LR operator?:(bool, L , R );
где LR является результатом обычных арифметических преобразований между типами L и R.
Один из этих встроенных элементов будет int operator?:(bool, int, int)
. Так как std::integral_constant<int, V>
имеет operator int()
, это является жизнеспособным преобразованием для обоих аргументов.
Мы продолжаем в [expr.cond]:
В противном случае применяются указанные таким образом преобразования, а преобразованные операнды используются вместо исходных операндов для остальной части этого раздела.
Выполняются стандартные значения преобразования Lvalue-to-rvalue (4.1), преобразования по методу "массив-в-указатель" (4.2) и стандартного преобразования функции-to-pointer (4.3) на втором и третьем операндах. После этих преобразований будет выполнено одно из следующих условий:
- Второй и третий операнды имеют один и тот же тип; результат этого типа и объект результата инициализируется с использованием выбранного операнда.
В этот момент второй и третий операнды имеют один и тот же тип: int
. Таким образом, объект результата инициализируется как int
, и выражение хорошо сформировано.