Pergunta

class Foo {
  public:
  explicit Foo(double item) : x(item) {}

  operator double() {return x*2.0;}

  private:
  double x;
}

double TernaryTest(Foo& item) {
  return some_condition ? item : 0;
}

Foo abc(3.05);
double test = TernaryTest(abc);

No exemplo acima, por que é o teste igual a 6 (em vez de 6,1) se some_condition é verdade?

A alteração do código como abaixo do valor de retorno de 6,1

double TernaryTest(Foo& item) {
  return some_condition ? item : 0.0; // note the change from 0 to 0.0
}

Parece que (no exemplo original) o valor de retorno de Foo :: operador duplo é convertido para um int e depois voltar para um duplo. Por quê?

Foi útil?

Solução

Os cheques operador condicional conversões em ambas as direções. Neste caso, uma vez que o seu construtor é explícita (por isso o ?: não é ambígua), a conversão de Foo para int é usado, utilizando a sua função de conversão que se converte em double: que funciona, porque depois de aplicar a função de conversão, uma conversão padrão que converte o double para int (truncagem) segue. O resultado da ?: no seu caso é int, e tem o valor 6.

No segundo caso, uma vez que o tipo de operando tem double, nenhuma conversão de fuga para int tem lugar, e, portanto, o tipo de resultado de ?: tem tipo double com o valor esperado.

Para entender as conversões "desnecessários", você tem que entender que expressões como seu ?: são avaliados "context-free": Na determinação do valor e tipo do mesmo, o compilador não considera que é o operando de um return para uma função retornando um double.


Edit: O que acontece se o seu construtor é implícita ? A expressão ?: será ambíguo, porque você pode converter um int a um rvalue do tipo Foo (usando o construtor), e um Foo a um rvalue do tipo int (usando a função de conversão). diz The Standard

Usando este processo, é determinado se o segundo operando pode ser convertido para coincidir com a terceira operando, e se o terceiro operando pode ser convertido para coincidir com o segundo operando. Se ambos podem ser convertidos, ou pode ser convertido, mas a conversão é ambíguo, o programa está mal-formado.


Os parágrafos explicando como a sua Foo é convertido em int:

5.16/3 sobre condition ? E1 : E2:

Caso contrário, se o segundo e terceiro operando ter tipos diferentes, e qualquer tipo de classe tem (possivelmente cv-qualificada), é feita uma tentativa para converter cada um desses operandos para o tipo do outro. [...] E1 pode ser convertido para corresponder E2 se E1 pode ser implicitamente convertido para o tipo que a expressão E2 teria se E2 foram convertidas para um rvalue (ou o tipo que tem, se E2 é um rvalue).

4.3 sobre "implicitamente convertido":

Uma expressão e podem ser convertidas, implicitamente, a um tipo T, se e apenas se o T t = e; declaração for bem formado, por alguma variável inventado t temporária.

8.5/14 sobre inicialização de cópia (T t = e;)

Se o tipo de fonte é um tipo de classe (possivelmente cv-qualificada), funções de conversão são considerados. As funções de conversão aplicáveis ??estão enumerados (13.3.1.5), e o melhor é escolhido por meio de resolução de sobrecarga (13,3). A conversão definido pelo utilizador, de modo seleccionado é chamado para converter a expressão inicializador para o objecto a ser inicializado. Se a conversão não pode ser feito ou é ambígua, a inicialização é mal-formado.

13.3.1.5 sobre os candidatos função de conversão

As funções de conversão de S e as suas classes de base são consideradas. Aqueles que não estão escondidas dentro S e tipo rendimento T ou um tipo que pode ser convertido no tipo T através de uma sequência de conversão de padrão (13.3.3.1.1) são funções candidatos.

Outras dicas

Este é coberto em confundir positivamente detalhe na secção 5.16 do padrão. A parte importante é o n.º 3. "Se E2 é um lvalue: E1 pode ser convertido para corresponder E2 se E1 pode ser convertido implicitamente (cláusula 4) para a 'referência a T2' tipo, sujeito à restrição de que na conversão do referência deve ligar-se directamente (8.5.3) para E1. "

Na expressão, a única lvalue é item, então a questão é se 0 (um int) pode ser implicitamente convertido para digitar Foo. Neste caso, não há nenhuma conversão implícita de qualquer outro tipo a um Foo, já que a função de conversão só está disponível é marcado explicit. Portanto, isso não funcionar, e nós seguimos com "se E2 é um rvalue, ou se a conversão acima não pode ser feito:" (pular a parte sobre se ambos têm tipo de classe) "Caso contrário (ou seja, se E1 ou E2 tem um tipo nonclass, ou se ambos têm tipos de classes, mas as classes subjacentes não são o mesmo ou um uma classe base da outra): E1 pode ser convertido para corresponder E1 se E1 pode ser implicitamente convertido para o tipo que a expressão E2 teria se E2 foram convertidas para um rvalue (ou o tipo que tem, se E2 é um rvalue) ".

Portanto, vemos que 0 é um rvalue, do tipo int. Podemos converter um Foo, já que pode converter implicitamente uma Foo a um double e daí para um int. Então:

"Usando este processo, é determinado se o segundo operando pode ser convertido para coincidir com o terceiro operando, e se o terceiro operando pode ser convertido para coincidir com o segundo operando. Se ambos podem ser convertidos, oor um pode ser convertido, mas a conversão é ambíguo, o programa está mal-formado. Se nem pode ser convertido, os operandos são deixados inalterado e ainda a verificação é realizada conforme descrito abaixo. Se exatamente uma conversão é possível, que a conversão é aplicada ao escolhido operando e os convertidos operando é usado no lugar do operando original para o restante desta seção ".

Uma vez que pode converter um Foo a um int, nós convertemos o Foo a um int para o restante da determinação. Temos agora dois ints como tipos de expressão, e pelo menos um é um rvalue.

eu posso ir com o parágrafo 5 e 6, mas eu acho que é bastante óbvio que a expressão tem tipo int.

Eu acho que os takeaways são:

  1. O seu compilador é funcionando de acordo com o padrão.

  2. As regras sobre o tipo de uma expressão condicional são demasiado complicado para ser facilmente aprendido. Não empurrar o envelope, porque você vai fazer um algum erro. (Além disso, este é exatamente o tipo de lugar onde um compilador pode falhar para implementar o padrão precisamente.)

  3. Tente especificar tipos de modo que tanto o segundo e terceiro expressão são do mesmo tipo. Em qualquer caso, para tentar evitar expressões que não são do tipo desejado.

O tipo da expressão ternário é determinada pelo tempo de compilação; não importa o que some_condition é em tempo de execução.

Eu acho que a pergunta é então: por que o compilador escolher int em vez de casal no primeiro exemplo?

operador ternário adivinha o tipo de seus argumentos. não pode converter item para int mas pode converter item para dobrar que então convertidos para int.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top