Compilando el siguiente código y obtuve el error de type illegal
.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
No puede usar una cadena en switch
o case
. ¿Por qué? ¿Hay alguna solución que funcione bien para admitir una lógica similar a encender cadenas?
c++
string
switch-statement
yesraaj
fuente
fuente
QMetaEnum
Respuestas:
El motivo tiene que ver con el sistema de tipos. C / C ++ realmente no admite cadenas como tipo. Es compatible con la idea de una matriz de caracteres constante, pero realmente no comprende completamente la noción de una cadena.
Para generar el código para una instrucción switch, el compilador debe comprender lo que significa que dos valores sean iguales. Para elementos como entradas y enumeraciones, esta es una comparación trivial de bits. Pero, ¿cómo debería el compilador comparar 2 valores de cadena? Sin distinción de mayúsculas y minúsculas, insensible, consciente de la cultura, etc. Sin un conocimiento completo de una cadena, esto no se puede responder con precisión.
Además, las instrucciones de cambio de C / C ++ generalmente se generan como tablas de ramificación . No es tan fácil generar una tabla de ramificación para un interruptor de estilo de cadena.
fuente
std::string
se agregaron literales. Es sobre todo histórico. Sin embargo, un problema que viene a la mente es que con la formaswitch
trabaja actualmente, duplicarcase
s debe ser detectada en tiempo de compilación; sin embargo, esto podría no ser tan fácil para las cadenas (teniendo en cuenta la selección de la configuración regional en tiempo de ejecución, etc.). Supongo que tal cosa debería requerirconstexpr
casos o agregar un comportamiento no especificado (nunca es algo que queramos hacer).std::string
valores o incluso unostd::string
con una matriz de caracteres constantes (es decir, mediante el uso de operator ==) no hay ninguna razón técnica que impida que el compilador genere una declaración de cambio para cualquier tipo que proporcione ese operador. Abriría algunas preguntas sobre cosas como la vida útil de las etiquetas, pero en general, esto es principalmente una decisión de diseño del lenguaje, no una dificultad técnica.Como se mencionó anteriormente, a los compiladores les gusta construir tablas de búsqueda que optimicen las
switch
declaraciones a un tiempo cercano a O (1) siempre que sea posible. Combine esto con el hecho de que el lenguaje C ++ no tiene un tipo de cadena,std::string
es parte de la Biblioteca estándar que no es parte del lenguaje per se.Ofreceré una alternativa que quizás desee considerar, la he usado en el pasado con buenos resultados. En lugar de cambiar la cadena en sí, cambie el resultado de una función hash que usa la cadena como entrada. Su código será casi tan claro como cambiar la cadena si está utilizando un conjunto predeterminado de cadenas:
Hay un montón de optimizaciones obvias que siguen más o menos lo que el compilador de C haría con una declaración de cambio ... es curioso cómo sucede eso.
fuente
C ++
función hash constexpr:
fuente
operator ""
para hacer que el código sea más hermoso.constexpr inline unsigned int operator "" _(char const * p, size_t) { return hash(p); }
Ycase "Peter"_: break;
Actualización de C ++ 11 de aparentemente no @MarmouCorp arriba pero http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Utiliza dos mapas para convertir entre las cadenas y la enumeración de la clase (mejor que la enumeración simple porque sus valores tienen un alcance dentro de ella, y la búsqueda inversa de mensajes de error agradables).
El uso de static en el código codeguru es posible con el soporte del compilador para las listas de inicializadores, lo que significa VS 2013 plus. gcc 4.8.1 estaba bien con él, no estoy seguro de cuánto más atrás sería compatible.
...
fuente
El problema es que, por razones de optimización, la instrucción switch en C ++ no funciona en nada más que en tipos primitivos, y solo puede compararlos con constantes de tiempo de compilación.
Presumiblemente, la razón de la restricción es que el compilador puede aplicar alguna forma de optimización compilando el código en una instrucción cmp y un goto donde la dirección se calcula en función del valor del argumento en tiempo de ejecución. Dado que la ramificación y los bucles no funcionan bien con las CPU modernas, esta puede ser una optimización importante.
Para evitar esto, me temo que tendrá que recurrir a declaraciones if.
fuente
std::string
y que otros sean los primeros ciudadanos en el idioma y los apoyen en la declaración de cambio con un algoritmo eficiente.std::map
+ C ++ 11 patrón lambdas sin enumeracionesunordered_map
para el potencial amortizadoO(1)
: ¿Cuál es la mejor manera de usar un HashMap en C ++?Salida:
Uso dentro de métodos con
static
Para usar este patrón de manera eficiente dentro de las clases, inicialice el mapa lambda estáticamente, o de lo contrario paga
O(n)
cada vez para construirlo desde cero.Aquí podemos salir con la
{}
inicialización de unastatic
variable de método: variables estáticas en métodos de clase , pero también podríamos usar los métodos descritos en: ¿ constructores estáticos en C ++? Necesito inicializar objetos estáticos privadosEra necesario transformar la captura de contexto lambda
[&]
en un argumento, o eso habría sido indefinido: const lambda automática estática utilizada con captura por referenciaEjemplo que produce el mismo resultado que el anterior:
fuente
switch
declaración. La duplicación de valores de casos en unaswitch
declaración es un error de tiempo de compilación. El usostd::unordered_map
silencioso acepta valores duplicados.En C ++ y C, los conmutadores solo funcionan en tipos enteros. Use una escalera if else en su lugar. C ++ obviamente podría haber implementado algún tipo de declaración rápida para cadenas: supongo que nadie pensó que valía la pena, y estoy de acuerdo con ellas.
fuente
Por qué no? Puede usar la implementación del interruptor con una sintaxis equivalente y la misma semántica. El
C
lenguaje no tiene objetos y objetos de cadenas en absoluto, pero las cadenas enC
son cadenas terminadas en nulo a las que hace referencia el puntero. ElC++
lenguaje tiene la posibilidad de realizar funciones de sobrecarga para comparar objetos o verificar la igualdad de los objetos. ComoC
yaC++
es lo suficientemente flexible como para tener tal interruptor para las cadenas deC
lenguaje y de objetos de cualquier tipo que comparaison apoyo o la igualdad de verificación deC++
la lengua. Y los modernosC++11
permiten que esta implementación del interruptor sea lo suficientemente efectiva.Su código será así:
Es posible utilizar tipos más complicados, por ejemplo,
std::pairs
o cualquier estructura o clase que admita operaciones de igualdad (o comarizaciones para el modo rápido ).Caracteristicas
Sintax diferencias con el cambio de idioma es
Para el
C++97
lenguaje se utiliza la búsqueda lineal. Para un modoC++11
más moderno posible de utilizar laquick
búsqueda de árbol de wuth donde la declaración de devolución en CASE no se permite. LaC
implementación del lenguaje existe dondechar*
se utilizan comparaciones de cadenas de tipo y terminadas en cero.Lea más sobre la implementación de este interruptor.
fuente
Para agregar una variación usando el contenedor más simple posible (sin necesidad de un mapa ordenado) ... No me molestaría con una enumeración: solo coloque la definición del contenedor inmediatamente antes del interruptor para que sea fácil ver qué número representa cuyo caso.
Esto hace una búsqueda hash en el
unordered_map
y usa el asociadoint
para conducir la declaración de cambio. Debería ser bastante rápido. Tenga en cuenta queat
se usa en lugar de[]
, ya que hice ese contenedorconst
. El uso[]
puede ser peligroso: si la cadena no está en el mapa, creará una nueva asignación y puede terminar con resultados indefinidos o un mapa en continuo crecimiento.Tenga en cuenta que la
at()
función generará una excepción si la cadena no está en el mapa. Por lo tanto, es posible que desee probar primero usandocount()
.La versión con una prueba para una cadena indefinida sigue:
fuente
Creo que la razón es que en C las cadenas no son tipos primitivos, como dijo tomjen, piense en una cadena como una matriz de caracteres, por lo que no puede hacer cosas como:
fuente
En c ++ las cadenas no son ciudadanos de primera clase. Las operaciones de cadena se realizan a través de la biblioteca estándar. Creo que esa es la razón. Además, C ++ utiliza la optimización de la tabla de ramificación para optimizar las declaraciones de mayúsculas y minúsculas. Echa un vistazo al enlace.
http://en.wikipedia.org/wiki/Switch_statement
fuente
En C ++ solo puede usar una instrucción switch en int y char
fuente
long
ylong long
, que no se convertirá enint
. No hay riesgo de truncamiento allí.fuente
en muchos casos, puede obtener un trabajo adicional extrayendo el primer carácter de la cadena y activándolo. puede terminar teniendo que hacer un cambio anidado en charat (1) si sus casos comienzan con el mismo valor. Sin embargo, cualquiera que lea su código agradecería una pista porque la mayoría probablemente solo si-si-si
fuente
Solución más funcional al problema del interruptor:
fuente
No puede usar la cadena en la caja del interruptor. Solo se permiten int y char. En su lugar, puede probar enum para representar la cadena y usarla en el bloque de mayúsculas y minúsculas como
Úselo en la declaración de caso swich.
fuente
Los interruptores solo funcionan con tipos integrales (int, char, bool, etc.). ¿Por qué no usar un mapa para emparejar una cadena con un número y luego usar ese número con el interruptor?
fuente
Eso es porque C ++ convierte los interruptores en tablas de salto. Realiza una operación trivial en los datos de entrada y salta a la dirección correcta sin comparar. Como una cadena no es un número, sino una matriz de números, C ++ no puede crear una tabla de salto a partir de ella.
(código de wikipedia https://en.wikipedia.org/wiki/Branch_table )
fuente
cmp
/jcc
implementación puede ser tan válida de acuerdo con el Estándar C ++.