Тип возврата '?:' (Троичный условный оператор)
Почему первый возвращает ссылку?
int x = 1;
int y = 2;
(x > y ? x : y) = 100;
Пока второго нет?
int x = 1;
long y = 2;
(x > y ? x : y) = 100;
Собственно, второе вообще не компилировалось - "не осталось значения от назначения".
4 ответа
У выражений нет возвращаемых типов, у них есть тип и, как известно из последнего стандарта 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)
Тип тройной ?:
Выражение является общим типом его второго и третьего аргумента. Если оба типа одинаковы, вы получите ссылку обратно. Если они конвертируемы друг в друга, один выбирается, а другой конвертируется (в этом случае повышается). Поскольку вы не можете вернуть ссылку lvalue на временную переменную (преобразованную / повышенную переменную), ее тип является типом значения.
Он не может вернуть значение 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
В противном случае результат является rvalue. Если второй и третий операнды не имеют один и тот же тип и оба имеют (возможно, cv-квалифицированный) тип класса, разрешение перегрузки используется для определения преобразований (если они есть), которые должны применяться к операндам (13.3.1.2, 13.6), Если не удается разрешить перегрузку, программа работает некорректно. В противном случае применяются определенные таким образом преобразования, а преобразованные операнды используются вместо исходных операндов в оставшейся части этого раздела.
Еще один пример
int x = 1;
int y = 2;
long z = 3;
(true ? x : y) = 100; // x & y are SAME type so it returns Lvalue(so it can be LHS of =). x = 100, y = 2
(false ? x : y) = 100; // x & y are SAME type so it returns Lvalue(so it can be LHS of =). x = 1 , y = 100
// (true ? x : z) = 100; // Error: x & z are DIFFERENT types so it returns Rvalue (so it can NOT be LHS of =)
// (false ? x : z) = 100; // Error: x & z are DIFFERENT types so it returns Rvalue (so it can NOT be LHS of =)