Pregunta

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);

En el ejemplo anterior, ¿por qué la prueba es igual a 6 (en lugar de 6.1) si alguna_condición es verdadera?

Cambiar el código como se muestra a continuación devuelve un valor de 6.1

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

Parece que (en el ejemplo original) el valor de retorno de Foo :: operator double se convierte en int y luego vuelve a double. ¿Por qué?

¿Fue útil?

Solución

El operador condicional verifica las conversiones en ambas direcciones. En este caso, dado que su constructor es explícito (por lo que ?: no es ambiguo), se utiliza la conversión de Foo a int , utilizando su función de conversión que se convierte en double : eso funciona, porque después de aplicar la función de conversión, una conversión estándar que convierte el double en int (truncamiento ) sigue. El resultado de ?: en su caso es int , y tiene el valor 6 .

En el segundo caso, dado que el operando tiene el tipo double , no se realiza dicha conversión final a int y, por lo tanto, el tipo de resultado de ?: tiene el tipo double con el valor esperado.

Para comprender lo innecesario conversiones, debe comprender que expresiones como su ?: se evalúan "sin contexto": al determinar el valor y el tipo, el compilador no considera que es el operando de un < code> return para una función que devuelve un double .


Editar: ¿Qué sucede si su constructor es implícito ? La expresión ?: será ambigua, porque puede convertir un int en un valor de tipo Foo (usando el constructor) y un < code> Foo a un valor de tipo int (usando la función de conversión). El estándar dice

  

Usando este proceso, se determina si el segundo operando se puede convertir para que coincida con el tercer operando, y si el tercer operando se puede convertir para que coincida con el segundo operando. Si ambos se pueden convertir, o uno se puede convertir pero la conversión es ambigua, el programa está mal formado.


Párrafos que explican cómo su Foo se convierte en int :

5.16 / 3 sobre la condición de ? E1: E2 :

  

De lo contrario, si el segundo y el tercer operando tienen tipos diferentes, y cualquiera de ellos tiene un tipo de clase (posiblemente calificado para cv), se intenta convertir cada uno de esos operandos al tipo del otro. [...] E1 se puede convertir para que coincida con E2 si E1 se puede convertir implícitamente al tipo que tendría la expresión E2 si E2 se convirtiera en un valor r (o el tipo que tiene, si E2 es un valor r).

4.3 sobre " convertido implícitamente " ;:

  

Una expresión e se puede convertir implícitamente a un tipo T si y solo si la declaración T t = e; está bien formada, para alguna variable temporal inventada t.

8.5 / 14 sobre la inicialización de la copia ( T t = e; )

  

Si el tipo de origen es un tipo de clase (posiblemente calificado para cv), se consideran las funciones de conversión. Las funciones de conversión aplicables se enumeran (13.3.1.5), y la mejor se elige mediante la resolución de sobrecarga (13.3). La conversión definida por el usuario así seleccionada se llama para convertir la expresión inicializadora en el objeto que se está inicializando. Si la conversión no se puede hacer o es ambigua, la inicialización está mal formada.

13.3.1.5 sobre los candidatos a la función de conversión

  

Se consideran las funciones de conversión de S y sus clases base. Los que no están ocultos dentro de S y producen el tipo T o un tipo que se puede convertir al tipo T mediante una secuencia de conversión estándar (13.3.3.1.1) son funciones candidatas.

Otros consejos

Esto está cubierto con detalles positivamente confusos en la sección 5.16 de la norma. La parte importante está en el párrafo 3. " Si E2 es un valor l: E1 se puede convertir para que coincida con E2 si E1 se puede convertir implícitamente (cláusula 4) al tipo 'referencia a T2', sujeto a la restricción que en la conversión la referencia debe enlazar directamente (8.5.3) a E1. "

En la expresión, el único lvalue es item , por lo que la pregunta es si 0 (an int) se puede convertir implícitamente al tipo Foo . En este caso, no hay conversión implícita de ningún otro tipo a un Foo , ya que la única función de conversión disponible está marcada explicit . Por lo tanto, eso no funciona, y seguimos con " si E2 es un valor r, o si la conversión anterior no se puede hacer: " (omitiendo la parte sobre si ambos tienen tipo de clase) " De lo contrario (es decir, si E1 o E2 tiene un tipo que no es de clase, o si ambos tienen tipos de clase pero las clases subyacentes no son iguales o una clase base de la clase otro): E1 se puede convertir para que coincida con E1 si E1 se puede convertir implícitamente al tipo que tendría la expresión E2 si E2 se convirtiera en un valor r (o el tipo que tiene, si E2 es un valor r). "

Por lo tanto, vemos que 0 es un valor r, de tipo int . Podemos convertir un Foo , ya que podemos convertir implícitamente un Foo en un double y de ahí en un int . Entonces:

" Usando este proceso, se determina si el segundo operando se puede convertir para que coincida con el tercer operando, y si el tercer operando se puede convertir para que coincida con el segundo operando. Si ambos se pueden convertir, o uno se puede convertir, pero la conversión es ambigua, el programa está mal formado. Si ninguno de los dos se puede convertir, los operandos se dejan sin cambios y se realiza una verificación adicional como se describe a continuación. Si es posible exactamente una conversión, esa conversión se aplica al operando elegido y el operando convertido se usa en lugar del operando original para el resto de esta sección. & Quot;

Ya que podemos convertir un Foo en un int , convertimos el Foo en un int para el resto de la determinación. Ahora tenemos dos int s como tipos de expresión, y al menos uno es un valor r.

Puedo continuar con los párrafos 5 y 6, pero creo que es bastante obvio que la expresión tiene el tipo int .

Creo que las conclusiones son:

  1. Su compilador funciona de acuerdo con el estándar.

  2. Las reglas sobre el tipo de una expresión condicional son demasiado complicadas para ser fácilmente aprendidas. No empujes el sobre, porque en algún momento cometerás un error. (Además, este es exactamente el tipo de lugar donde un compilador podría fallar al implementar el estándar con precisión).

  3. Intente especificar tipos para que tanto la segunda como la tercera expresión sean del mismo tipo. En cualquier caso, intente evitar expresiones que no sean del tipo deseado.

El tipo de la expresión ternaria se determina en tiempo de compilación; no importa lo que sea alguna condición en tiempo de ejecución.

Supongo que la pregunta es: ¿por qué el compilador elige int en lugar de doble en el primer ejemplo?

El operador ternario

adivina el tipo a partir de sus argumentos. no puede convertir el elemento a int pero puede convertir el elemento al doble que luego convierte a int.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top