Tipo de retorno de ‘?:’ (Operador condicional ternário)

Por que o primeiro retorna uma referência?

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

Enquanto o segundo não?

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

Na verdade, o segundo não compilou nada – “não lvalue à esquerda da atribuição”.

As expressões não têm tipos de retorno, elas têm um tipo e – como é conhecido no padrão C ++ mais recente – uma categoria de valor.

Uma expressão condicional pode ser um lvalue ou um rvalue . Esta é sua categoria de valor. (Isto é um pouco de uma simplificação, em C++11 nós temos lvalues, xvalues ​​e prvalues.)

Em termos muito amplos e simples, um lvalue se refere a um object na memory e um rvalue é apenas um valor que pode não estar necessariamente anexado a um object na memory.

Uma expressão de atribuição atribui um valor a um object, de modo que a coisa a ser atribuída deve ser um lvalue .

Para uma expressão condicional ( ?: 🙂 Ser um lvalue (novamente, em termos amplos e simples), o segundo e terceiro operandos devem ser lvalores do mesmo tipo . Isso ocorre porque a categoria de tipo e valor de uma expressão condicional é determinada em tempo de compilation e deve ser apropriada, independentemente de a condição ser verdadeira ou não. Se um dos operandos precisar ser convertido em um tipo diferente para corresponder ao outro, a expressão condicional não poderá ser um lvalue, pois o resultado dessa conversão não seria um lvalue .

ISO / IEC 14882: referências de 2011:

3.10 [basic.lval] Lvalues ​​e rvalues ​​(sobre categorias de valor)

5.15 [expr.cond] Operador condicional (regras para o tipo e categoria de valor que uma expressão condicional possui)

5.17 [expr.ass] Operadores de atribuição e atribuição compostos (exigência de que o lhs de uma atribuição deva ser um valor modificável)

O tipo da expressão ternária é o tipo comum de seu segundo e terceiro argumentos. Se os dois tipos forem iguais, você recebe uma referência de volta. Se eles são convertíveis entre si, um é escolhido e o outro é convertido (promovido neste caso). Como você não pode retornar uma referência lvalue a um temporário (a variável convertida / promovida), seu tipo é um tipo de valor.

Ele não pode retornar um lvalue, pois ele terá que promover implicitamente o tipo de x para corresponder ao tipo de y (já que ambos os lados de : não são do mesmo tipo) e, com isso, ele deve criar um temporário.


O que diz o padrão? ( n1905 )

Expressões 5.17 Operadores de atribuição e designação composta

5,17 / 3

Se o segundo e o terceiro operando tiverem tipos diferentes e tiverem um tipo de class (possivelmente qualificado por cv), será feita uma tentativa de converter cada um desses operandos para o tipo do outro. O processo para determinar se um operando expressão E1 do tipo T1 pode ser convertido para corresponder a um operando expressão E2 do tipo T2 é definido da seguinte maneira:

– Se E2 é um lvalor: E1 pode ser convertido para combinar E2 se E1 pode ser implicitamente convertido (cláusula 4) para o tipo “referência a T2”, sujeito à restrição de que na conversão a referência deve se ligar diretamente (8.5.3 ) para E1.

– Se E2 for um rvalue, ou se a conversão acima não puder ser feita:

– se E1 e E2 tiverem tipo de class e os tipos de class subjacentes forem iguais ou se for uma class base do outro: E1 pode ser convertido para corresponder a E2 se a class de T2 for do mesmo tipo ou uma class base de , a class de T1, e a qualificação cv de T2 é a mesma qualificação de cv que, ou uma maior qualificação cv do que, a qualificação cv de T1. Se a conversão for aplicada, E1 é alterado para um rvalue do tipo T2 que ainda se refere ao object da class de origem original (ou ao subobject apropriado). [ Nota: isto é, nenhuma cópia é feita. – end note ] por cópia inicializando um temporário do tipo T2 de E1 e usando esse temporário como o operando convertido.

Caso contrário (ou seja, se E1 ou E2 não tem um tipo de class, ou se ambos têm tipos de class, mas as classs subjacentes não são iguais ou uma class base do outro): E1 pode ser convertido para corresponder a E2 se E1 puder ser implicitamente convertido para o tipo que a expressão E2 teria se E2 fosse convertido em um rvalue (ou o tipo que possui, se E2 for um rvalue).

Usando esse processo, é determinado se o segundo operando pode ser convertido para corresponder ao terceiro operando e se o terceiro operando pode ser convertido para corresponder ao segundo operando. Se ambos podem ser convertidos, ou um pode ser convertido, mas a conversão é ambígua, o programa é mal formado. Se nenhum dos dois puder ser convertido, os operandos serão mantidos inalterados e uma verificação adicional será executada conforme descrito abaixo. Se exatamente uma conversão for possível, essa conversão será aplicada ao operando escolhido e o operando convertido será usado no lugar do operando original para o restante desta seção.


5,17 / 4

Se o segundo e o terceiro operandos forem lvalues ​​e tiverem o mesmo tipo, o resultado será desse tipo e será um lvalue e será um campo de bits se o segundo ou terceiro operando for um campo de bits ou se ambos forem bit- Campos.


5,17 / 5

Caso contrário, o resultado é um valor. Se o segundo e o terceiro operandos não tiverem o mesmo tipo e um tipo de class (possivelmente qualificado pelo cv), a resolução de sobrecarga será usada para determinar as conversões (se houver) a serem aplicadas aos operandos (13.3.1.2, 13.6) . Se a resolução de sobrecarga falhar, o programa está mal formado. Caso contrário, as conversões assim determinadas serão aplicadas e os operandos convertidos serão usados ​​no lugar dos operandos originais para o restante desta seção.