Тип возврата '?:' (Троичный условный оператор)


208

Почему первый возвращает ссылку?

int x = 1;
int y = 2;
(x > y ? x : y) = 100;

Пока второго нет?

int x = 1;
long y = 2;
(x > y ? x : y) = 100;

Собственно, второе вообще не компилировалось - «не осталось значения от назначения».


1
хм, это как найти особый случай для выпечки хлеба, не приходил к этому однажды
Ulterior


Поскольку присвоение типа выражению подразумевает приведение хотя бы одного термина, этот термин больше не будет l-значением.
Ив Дауст

Ответы:


173

У выражений нет возвращаемых типов, у них есть тип и, как известно из последнего стандарта C ++, категория значений.

Условное выражение может быть lvalue или rvalue . Это его ценностная категория. (Это несколько упрощение, у C++11нас есть lvalues, xvalues ​​и prvalues.)

В очень широких и простых терминах lvalue относится к объекту в памяти, а rvalue - это просто значение, которое необязательно может быть присоединено к объекту в памяти.

Выражение присваивания присваивает значение объекту, поэтому назначаемая вещь должна быть lvalue .

Чтобы условное выражение ( ?:) было lvalue (опять же, в широком и простом выражении), второй и третий операнды должны быть lvalue одного и того же типа . Это связано с тем, что тип и категория значений условного выражения определяются во время компиляции и должны соответствовать тому, является ли условие истинным. Если один из операндов должен быть преобразован в другой тип, чтобы соответствовать другому, то условное выражение не может быть lvalue, так как результат этого преобразования не будет lvalue .

Ссылки ИСО / МЭК 14882: 2011:

3.10 [basic.lval] Lvalues ​​и rvalues ​​(о категориях значений)

5.15 [expr.cond] Условный оператор (правила для того, какую категорию и категорию значений имеет условное выражение)

5.17 [expr.ass] Операторы присваивания и составного присваивания (требование, чтобы lhs присваивания были модифицируемыми lvalue)


3
И читая xvalue и prvalue (так как я не слышал о них до вашего ответа), я наткнулся на этот полезный пост SO: stackoverflow.com/questions/3601602/…
пушистый

an rvalue is just a value that may not necessarily be *attached* to an object in memory.Можете ли вы объяснить это более простым термином? , И что вы подразумеваете под type and value *category*? Спасибо
Mr.Anubis

@SoulReaper: prvalue, xvalue, glvalueкатегории значений.
Xeo

@Xeo Я ценю помощь, но можете ли вы сказать, что он подразумевает под r-значением, это просто значение, которое необязательно может быть привязано к объекту в памяти. ? с примером?
Анубис

@SoulReaper: Я думаю , что он говорит о таких вещах , как true, this, enumценность. Эти вещи являются значениями («чистыми» значениями), но не живут в памяти.
Xeo

57

Тип тройной ?: выражения является общим типом его второго и третьего аргумента. Если оба типа одинаковы, вы получите ссылку обратно. Если они конвертируемы друг в друга, один выбирается, а другой конвертируется (повышается в этом случае). Поскольку вы не можете вернуть ссылку lvalue на временную переменную (преобразованную / повышенную переменную), ее тип является типом значения.


но у больше, чем х, поэтому нет необходимости в продвижении для этого конкретного случая, он может вернуть ссылку на у. Хм ... Но я согласен, это было бы странно.
Йола

1
@ Mr.TAMER: Я бы лучше копаться в стандарте. : <
Xeo

3
@Yola: Поскольку типы являются концепцией времени компиляции в C ++, фактическое возвращаемое значение выражения не имеет значения.
Xeo

1
Вы не получите ссылку назад, вы получите lvalue.
Сума

1
@Xeo: Не в терминологии C ++, хотя;)
Себастьян Мах

19

Он не может возвратить lvalue, так как ему придется неявно указывать тип, xсоответствующий типу y(поскольку обе стороны :не одного типа), и с этим он должен создать временный тип.


Что говорит стандарт? ( n1905 )

Выражения 5.17 Операции присваивания и составного присваивания

5,17 / 3

Если второй и третий операнды имеют разные типы, и любой имеет (возможно, cv-квалифицированный) тип класса, делается попытка преобразовать каждый из этих операндов в тип другого. Процесс определения, можно ли преобразовать выражение E1 операнда типа T1 для соответствия выражению E2 операнда типа T2, определяется следующим образом:

- Если E2 является lvalue: E1 может быть преобразовано для соответствия E2, если E1 может быть неявно преобразовано (пункт 4) в тип «ссылка на T2», при условии ограничения, что в преобразовании ссылка должна связываться напрямую (8.5.3 ) к E1.

- Если E2 является значением r или если приведенное выше преобразование не может быть выполнено:

- если E1 и E2 имеют тип класса, и базовые типы классов совпадают или один является базовым классом другого: E1 может быть преобразован в соответствие E2, если класс T2 того же типа, что и базовый класс класс T1 и cv-квалификация T2 - это та же cv-квалификация, что и cv-квалификация, которая выше, чем cv-квалификация T1. Если преобразование применяется, E1 заменяется на значение типа T2, которое все еще ссылается на исходный объект класса источника (или соответствующий подобъект). [ Примечание: то есть копия не сделана. - конец примечания ] путем инициализации копии временного типа T2 из E1 и использования этого временного объекта в качестве преобразованного операнда.

В противном случае (т.е. если E1 E2 имеет тип, отличный от класса, или если оба имеют типы классов, но базовые классы не являются одинаковыми или являются базовым классом другого): E1 может быть преобразован в E2, если E1 может быть неявно преобразуется в тип, который будет иметь выражение E2, если E2 будет преобразовано в значение r (или тип, который он имеет, если E2 является значением r).

Используя этот процесс, определяется, может ли второй операнд быть преобразован, чтобы соответствовать третьему операнду, и может ли третий операнд быть преобразован, чтобы соответствовать второму операнду. Если оба могут быть преобразованы, или один может быть преобразован, но преобразование неоднозначно, программа плохо сформирована. Если ни один из них не может быть преобразован, операнды остаются без изменений, и дальнейшая проверка выполняется, как описано ниже. Если возможно только одно преобразование, это преобразование применяется к выбранному операнду, а преобразованный операнд используется вместо исходного операнда в оставшейся части этого раздела.


5,17 / 4

Если второй и третий операнды являются l-значениями и имеют один и тот же тип, результат имеет тот же тип и является l-значением, и это битовое поле, если второй или третий операнд является битовым полем, или если оба являются бит-полем поля.


5.17 / 5

В противном случае результатом является значение. Если второй и третий операнды не имеют одинаковый тип и оба имеют (возможно, cv-квалифицированный) тип класса, разрешение перегрузки используется для определения преобразований (если они есть), которые должны применяться к операндам (13.3.1.2, 13.6) , Если не удается разрешить перегрузку, программа работает некорректно. В противном случае применяются определенные таким образом преобразования, а преобразованные операнды используются вместо исходных операндов в оставшейся части этого раздела.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.