Есть фрагмент кода:
int x = 0;
const int y = 0;
decltype(auto) z = true ? x : y;
static_assert(std::is_same_v<const int&, decltype(z)>);
Согласно [expr.cond], второй операнд x
неявно преобразуется в const int&
, а третий операнд y
имеет тип const int
, который отличается от const int&
, поэтому программа должна быть неправильно сформирована.
Очевидно, я ошибаюсь, но я не знаю, в чем дело? Или какой конкретный процесс определяет тип выражения как const int&
?
Конечно, y
имеет тип const int
, но он не появляется в выражении утверждения; что в этом неправильного? Согласно пункту 4.1 ссылочного документа, все условное выражение имеет ссылочный тип, поэтому z
объявляется как ссылка, которая удовлетворяет утверждению.
В конце раздела 4 сказано, что преобразование применяется к выбранному операнду, поэтому x
после преобразования имеет тип, отличный от y
, поэтому оставшийся подпункт, по-видимому, неприменим.
Это условное выражение регулируется [expr.cond]/4, которое я частично цитирую:
В противном случае, если второй и третий операнд имеют разные типы и либо имеют тип класса (возможно, с указанием cv), либо оба являются glvalues одной и той же категории значений и одного и того же типа, за исключением уточнения cv, предпринимается попытка сформировать последовательность неявного преобразования каждого из этих операндов в тип другого. [...] Предпринимаются попытки сформировать последовательность неявного преобразования из выражения операнда E1 типа T1 в целевой тип, связанный с типом T2 выражения операнда E2, следующим образом:
- Если E2 является lvalue, целевым типом является «ссылка lvalue на T2», но последовательность неявного преобразования может быть сформирована только в том случае, если ссылка будет напрямую привязана ([dcl.init.ref]) к glvalue.
- [...]
С помощью этого процесса определяется, может ли быть сформирована последовательность неявного преобразования из второго операнда в целевой тип, определенный для третьего операнда, и наоборот. Если обе последовательности могут быть сформированы или одна может быть сформирована, но это неоднозначная последовательность преобразования, программа является неправильной. Если последовательность преобразования не может быть сформирована, операнды остаются неизменными и дальнейшая проверка выполняется, как описано ниже. В противном случае, если может быть сформирована ровно одна последовательность преобразования, это преобразование применяется к выбранному операнду, и преобразованный операнд используется вместо исходного операнда в оставшейся части этого подраздела. [...]
x
и y
— это «glvalues одной и той же категории значений» (поскольку они оба являются lvalue) и одного типа, за исключением cv-квалификации, поэтому применим этот маркированный список. Принимая E1 за x
, а E2 за y
, поскольку y
является lvalue, применяется первый пункт ([expr.cond]/4.1): целевой тип, в который нужно преобразовать x
, — это «ссылка lvalue на const int
». Поскольку такое преобразование возможно (а обратное преобразование невозможно(1)), преобразование применяется.
После этого шага условное выражение выглядит как true ? static_cast<const int&>(x) : y
.
Обратите внимание, что второй и третий операнд теперь являются значениями типа const int
. Выражения не имеют ссылочного типа; выражение, которое имеет ссылочный тип lvalue, на самом деле является просто lvalue ссылочного типа (см. [expr.type]/1).
Итак, мы переходим к [expr.cond]/5:
Если второй и третий операнды являются glvalues одной и той же категории значений и имеют один и тот же тип, результат имеет этот тип и категорию значения и является битовым полем, если второй или третий операнд является битовым полем или если оба являются битовыми полями.
Второй и третий операнд теперь являются glvalue одной и той же категории значений (lvalue) и того же типа (const int
), поэтому результатом является lvalue const int
, и все готово.
Когда вы применяете decltype
к такому выражению, оно дает вам тип, а затем добавляет к нему &
, чтобы сообщить вам, что выражение является lvalue ([dcl.type.decltype]/1.5).
(1) Обратное преобразование y
в «ссылку lvalue на int
» невозможно, поскольку при этом квалификатор const
будет отброшен.
«Выражения не имеют ссылочного типа...» У них действительно есть ссылочный тип, как утверждает и объясняет Скотт Мейерс. Обратите внимание, что он также сказал в своей статье/объяснении: «Совершенно очевидно, что выражения могут иметь ссылочный тип».
@user12002570 user12002570 У них есть ссылочный тип на короткое время, когда с ними невозможно что-либо сделать, прежде чем они будут настроены, поэтому я предпочитаю думать о [expr.type]/1 как о чистом правиле построения.
Да, технически выражения имеют ссылочный тип. Хотя неформально, как вы сказали, часто бывает полезно сразу подумать о типе после корректировки. Но мы все равно должны явно упомянуть, что стандарт позволяет выражениям иметь ссылочный тип (даже если это только до настройки и на короткое время).
почти уверен этот раздел означает, что
x
становитсяconst int
, поэтому обе стороны становятсяconst int
, аz
становитсяconst int&