Тип возврата '?:' (Троичный условный оператор) - PullRequest
206 голосов
/ 16 декабря 2011

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

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

В то время как второй нет?

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

На самом деле второй вообще не компилировался - "не осталось слева отназначение».

Ответы [ 3 ]

173 голосов
/ 16 декабря 2011

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

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

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

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

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

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

3.10 [basic.lval] L-значения и r-значения (о категориях значений)

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

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

56 голосов
/ 16 декабря 2011

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

18 голосов
/ 16 декабря 2011

Он не может вернуть 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

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


5,17 / 5

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

...