2011-03-26 8 views
15

Este es el trato. Tengo una clase estática que contiene varias funciones estáticas utilizadas para obtener entrada. La clase contiene una variable miembro privada estática para indicar si el usuario ingresó alguna información. Cada método de entrada comprueba si el usuario ingresó alguna información y establece la variable de estado en consecuencia. Creo que este sería un buen momento para usar el operador ternario. Lamentablemente, no puedo, porque al compilador no le gusta eso.¿Por qué las variables de miembros estáticos no funcionan bien con el operador ternario?

He replicado el problema, luego simplifiqué mi código tanto como fue posible para hacerlo más fácil de entender. Este no es mi código original.

Aquí está mi archivo de cabecera:

#include <iostream> 

using namespace std; 

class Test { 
public: 
    void go(); 
private: 
    static const int GOOD = 0; 
    static const int BAD = 1; 
}; 

Aquí está mi aplicación con el operador ternario:

#include "test.h" 

void Test::go() { 
    int num = 3; 
    int localStatus; 
    localStatus = (num > 2) ? GOOD : BAD; 
} 

Aquí es función principal:

#include <iostream> 
#include "test.h" 

using namespace std; 

int main() { 
    Test test = Test(); 
    test.go(); 
    return 0; 
} 

Cuando intento compilar esto, recibe este mensaje de error:

test.o: In function `Test::go()': 
test.cpp:(.text+0x17): undefined reference to `Test::GOOD' 
test.cpp:(.text+0x1f): undefined reference to `Test::BAD' 
collect2: ld returned 1 exit status 

Sin embargo, si reemplazo esto:

localStatus = (num > 2) ? GOOD : BAD; 

con esto:

if (num > 2) { 
    localStatus = GOOD; 
} else { 
    localStatus = BAD; 
} 

El código se compila y se ejecuta como se esperaba. ¿Qué oscura regla de C++ o caso de esquina de GCC es responsable de esta locura? (Estoy usando GCC 4.4.1 en Ubuntu 9.10.)

Respuesta

19

Esto es de acuerdo con el estándar C++. El operador ternario constituye un único valor l que se referirá a GOOD o BAD en tiempo de ejecución.La conversión lvalue a rvalue no se aplica inmediatamente a lvalue GOOD o BAD, y para ello necesita una definición de GOOD y BAD.

Consulte el informe de problemas del lenguaje central http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#712.

Como solución alternativa, se puede aplicar conversiones explícitas a int (que lee sus valores, haciendo con ello un valor-I a la conversión rvalue) o utilizar un operador que lee el valor, como +:

localStatus = (num > 2) ? +GOOD : +BAD; 
+0

¿Dónde se distingue la norma entre los dos casos? ¿No usan los dos "BUENO" y, por lo tanto, requieren una definición? Creo que es solo que gcc es lo suficientemente inteligente como para evitar el enlace externo en el caso de que lvalue 'GOOD' se use como el RHS de una tarea (y por lo tanto se convierte inmediatamente en rvaue-rvalue), pero no lo ha manejado en el caso de lvalue 'BUENO 'utilizado en un operador ternario, pero estoy dispuesto a creer que estoy equivocado. –

+0

@Steve si haces '+ GOOD' o' int a = GOOD', haces el lvalue para validar la conversión en el lvalue 'GOOD' (" directamente "- mira 3.2p2, que dice que en esos casos no lo haces" usa "la variable". Si haces 'int a = x? a: b; ', usted haría la conversión lvalor para rvaluar en el valor l que produce el operador ternario, no en el valor l que producen' a' y 'b'. –

+0

Ah, tu comentario a James responde mi pregunta. Se distinguen por el texto insertado en los borradores después de C++ 03 pero antes de que ese defecto se levantara en 2008. Creo que el estándar actual hace lo que dije. Tanto mi copia impresa como mi PDF de 2003 (E) dicen: "Se usa un objeto o función no sobrecargada si su nombre aparece en una expresión potencialmente evaluada". sin excepciones –

5
class Test { 
    static const int GOOD = 0; 
    static const int BAD = 1; 
}; 

Estos son sólo declaraciones ; no son definiciones. Es necesario proporcionar definiciones de las variables miembro estáticas, fuera de la definición de la clase, en una de sus archivos .cpp:

const int Test::GOOD; 
const int Test::BAD; 

Alternativamente, para constantes enteras, a menudo es más conveniente usar un enum:

class Test { 
    enum { 
     GOOD = 0, 
     BAD = 1 
    }; 
}; 
+2

¿Por qué la la falta de una definición molesta al operador ternario pero no al operador de asignación estándar? –

+0

No es tan simple como eso. Ver mi respuesta – TonyK

+0

@Evan: ¿De qué operador de asignación estás hablando? ¿El que le asigna valor a BUENO y MALO? –

1

Su código se ve bien para mí. Y ideone está de acuerdo: ver this link. Pero eso es con gcc-4.3.4. Sin embargo, mi gcc-4.4.0 no lo acepta. Entonces, sea cual sea el motivo, no es obvio.

Editado para agregar: La siguiente variante compila bajo gcc-4.4.0:

int localStatus = 42 ? GOOD : BAD; 

Recordatorio: el siguiente código no se compila:

int localStatus = (num == 42) ? GOOD : BAD; 

Así que alguien ha metido la pata algun lado.

+0

No estoy seguro si alguien jodió en alguna parte. Creo que, en el primer caso, lo único que sucede es que la expresión está optimizada. pero el verdadero problema sigue ahí. – Petr

Cuestiones relacionadas