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


208

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 foi compilado - "não foi o valor restante da tarefa".


1
hmm, isso é como encontrar um caso especial para cozer pão, não tinha veio a este uma vez
Ulterior


Como atribuir um tipo à expressão implicaria uma conversão de pelo menos um termo, esse termo não seria mais um valor l.
precisa

Respostas:


173

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 é a sua categoria de valor. (Isso é uma simplificação, C++11pois temos lvalues, xvalues ​​e prvalues.)

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

Uma expressão de atribuição atribui um valor a um objeto, portanto a coisa a ser atribuída deve ser um valor l .

Para que uma expressão condicional ( ?:) seja um lvalue (novamente, em termos amplos e simples), o segundo e o terceiro operandos devem ser lvalues do mesmo tipo . Isso ocorre porque o tipo e a categoria de valor de uma expressão condicional são determinados no momento da compilação e devem ser apropriados, 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 .

Referências ISO / IEC 14882: 2011:

3.10 [basic.lval] Valores e valores (sobre categorias de valor)

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

5.17 [expr.ass] Operadores de atribuição e atribuição composta (requisito de que os lhs de uma atribuição devam ser um valor modificável)


3
E ao ler sobre xvalue e prvalue (desde que eu não os tinha ouvido antes da sua resposta) me deparei com este post útil do SO: stackoverflow.com/questions/3601602/…
fluffy

an rvalue is just a value that may not necessarily be *attached* to an object in memory.Você pode explicar isso em termos mais simples? . Também o que você quer dizer com type and value *category*? Obrigado
Mr.Anubis 4/12/12

@SoulReaper: prvalue, xvalue, glvaluesão categorias de valor.
Xeo

@ Xeo Agradeço ajuda, mas você pode dizer o que ele quer dizer com rvalue é apenas um valor que pode não estar necessariamente associado a um objeto na memória. ? com exemplo?
Mr.Anubis

@SoulReaper: Eu acho que ele está falando coisas como true, this, enumvalores. Essas coisas são valores prévios (valores "puros"), mas não vivem na memória.
Xeo 10/10/12

57

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


mas y for maior que x, portanto, não há necessidade de promoção para este caso específico, ele pode retornar referência a y. Hmm ... Mas eu concordo, seria estranho.
Yola

1
@ Mr.TAMER: Prefiro vasculhar o padrão. : <
Xeo

3
@ Yola: Como os tipos são um conceito de tempo de compilação em C ++, o valor de retorno real da expressão não importa.
Xeo

1
Você não recebe uma referência de volta, recebe lvalue.
Suma

1
@ Xeo: Não na terminologia C ++, no entanto;)
Sebastian Mach

19

Ele não pode retornar um valor l, pois precisará implicitamente promover o tipo de xpara corresponder ao tipo de y(já que os dois lados :não são do mesmo tipo) e, com isso, deve criar um temporário.


O que diz o padrão? ( n1905 )

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

5.17 / 3

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

- Se E2 for um valor l: E1 pode ser convertido para corresponder a E2 se E1 puder ser implicitamente convertido (cláusula 4) no tipo "referência a T2", sujeito à restrição de que na conversão a referência deve se vincular diretamente (8.5.3 ) para E1.

- Se E2 for um rvalor ou se a conversão acima não puder ser feita:

- se E1 e E2 tiverem tipo de classe e os tipos de classe subjacentes forem os mesmos ou um for uma classe base da outra: E1 poderá ser convertido para corresponder a E2 se a classe de T2 for do mesmo tipo ou uma classe base de , a classe de T1 e a qualificação cv de T2 são a mesma qualificação cv que ou uma qualificação cv maior que a qualificação cv de T1. Se a conversão for aplicada, E1 será alterado para um rvalor do tipo T2 que ainda se refere ao objeto de classe de origem original (ou o subobjeto apropriado do mesmo). [ Nota: isto é, nenhuma cópia é feita. - nota final ] copiando-inicializando um temporário do tipo T2 de E1 e usando esse temporário como operando convertido.

Caso contrário (por exemplo, se E1ou E2 tiver um tipo não de classe ou se ambos tiverem tipos de classe, mas as classes subjacentes não forem iguais ou uma classe base da outra): E1 pode ser convertido para corresponder a E2 se E1 puder ser convertido implicitamente no tipo que a expressão E2 teria se E2 fosse convertido em um rvalue (ou no tipo que possui, se E2 for um rvalue).

Utilizando 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 puderem ser convertidos ou um puder ser convertido, mas a conversão for ambígua, o programa será mal formado. Se nenhum dos dois puder ser convertido, os operandos permanecerão inalterados e uma verificação adicional será realizada 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 pelo 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 o terceiro operando for um campo de bits ou se ambos forem de bit. Campos.


5.17 / 5

Caso contrário, o resultado é um rvalue. Se o segundo e o terceiro operando não tiverem o mesmo tipo e tiver um tipo de classe (possivelmente qualificado para 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 da sobrecarga falhar, o programa está mal formado. Caso contrário, as conversões assim determinadas são aplicadas e os operandos convertidos são usados ​​no lugar dos operandos originais pelo restante desta seção.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.