¿Por qué hay tantas clases de cadenas frente a std :: string?

56

Me parece que muchas bibliotecas C ++ más grandes terminan creando su propio tipo de cadena. En el código de cliente que o bien tiene que usar el uno de la biblioteca ( QString, CString, fbstringetc., estoy seguro de que nadie puede nombrar algunos) o mantener la conversión entre el tipo estándar y el que utiliza la biblioteca (que la mayoría de las veces implica al menos una copia).

Entonces, ¿hay algún error en particular o algo malo std::string(al igual que la auto_ptrsemántica era mala)? ¿Ha cambiado en C ++ 11?

Tamás Szelei
fuente
32
Se llama "Síndrome no inventado aquí".
Cat Plus Plus
10
@CatPlusPlus QString y CString ambos son anteriores a std :: string.
Gort the Robot
8
@ Cat Plus Plus: este síndrome no parece afectar a la clase Java String.
Giorgio
20
@Giorgio: los programadores de Java están demasiado ocupados inventando soluciones para las deficiencias del lenguaje como para preocuparse por las clases de cadenas (por cierto, Android reinventó la cadena).
Cat Plus Plus
99
@Giorgio: Probablemente se deba a que el soporte sintáctico codificado de Java para java.lang.String(falta de sobrecarga del operador, etc.) dificultaría el uso de cualquier otra cosa.
Caracol mecánico

Respuestas:

57

La mayoría de esas bibliotecas C ++ más grandes se iniciaron antes de que std::stringse estandarizara. Otros incluyen características adicionales que se estandarizaron tarde o que aún no se estandarizaron, como el soporte para UTF-8 y la conversión entre codificaciones.

Si esas bibliotecas se implementaran hoy, probablemente elegirían escribir funciones e iteradores que operen en std::stringinstancias.

Ben Voigt
fuente
55
El soporte para UTF-8 está estandarizado desde C ++ 98. De una manera tan incómoda y parcialmente definida de implementación que casi nadie parece poder usarlo
AProgrammer
99
@AProgrammer: charse garantiza que es lo suficientemente grande como para contener cualquier punto de código UTF-8. AFAIK, ese es el único "soporte" que proporcionó C ++ 98.
Ben Voigt
44
@AProgrammer: Ese soporte es realmente bastante inútil.
DeadMG
44
@AProgrammer Se puede decir que esa configuración regional está rota ya wchar_tque no es lo suficientemente grande como para representar todos los puntos de código Unicode. Además, hubo toda esta discusión sobre UTF-16 considerada dañina donde se hizo el argumento convincente de que UTF-8 debería usarse exclusivamente ...
Konrad Rudolph
66
@KonradRudolph, no es el sistema local el que está roto allí (la definición de wchar_t es "lo suficientemente amplia para cualquier conjunto de caracteres compatible"); Los sistemas que se han comprometido con 16 bits wchar_t al mismo tiempo se comprometieron a no admitir Unicode. Bueno, el culpable es Unicode, que primero garantizó que nunca usaría puntos de código que necesitan más de 16 bits, luego los sistemas se comprometen a un wchar_t de 16 bits, luego el cambio unicode necesita más de 16 bits.
Programador
39

String es la gran vergüenza de C ++.

Durante los primeros 15 años, no proporciona una clase de cadena en absoluto, lo que obliga a cada compilador en cada plataforma y a cada usuario a crear la suya propia.

Luego crea algo que está confundido acerca de si se supone que es una API de manipulación de cadena completa o solo un contenedor de caracteres STL, con algunos algoritmos que duplican los de un std :: Vector o son diferentes.

Cuando una operación de cadena obvia como replace () o mid () involucra un lío de iteradores que necesita introducir una nueva palabra clave 'auto' para mantener la declaración adecuada en una sola página y hace que la mayoría de las personas se rindan en todo el idioma .

Y luego tienes unicode 'support' y std :: wstring que es solo arghh .....

<despotricar> gracias - Me siento mucho mejor ahora.

Martin Beckett
fuente
12
@DeadMG: sí, y se estandarizó en 1998, 15 años después de que se inventó y 6 años después de que incluso MSFT lo usara. Sí, los iteradores son una forma útil de hacer que una matriz y una lista se vean iguales, ¿crees que son una forma obvia de manipular cadenas?
Martin Beckett
3
C con clases fue inventada en 1983. No C ++. Las únicas bibliotecas estándar son aquellas determinadas por Standard, lo cual, curiosamente, solo puede suceder una vez que tiene un Standard, por lo que la fecha más temprana posible para cualquier biblioteca Standard es 1998. Y los iteradores podrían considerarse exactamente iguales a los índices, pero fuertemente tipados. Estoy totalmente de acuerdo con el hecho de que los iteradores apestan en comparación con los rangos, pero eso no es realmente específico std::string. La falta de una clase String en 1983 no justifica tener más de ellos ahora.
DeadMG
8
Pensé que los iostreams eran la gran vergüenza de C ++ ...
Doug T.
18
@DeadMG La gente usaba algo llamado "C ++" durante muchos años antes de 1998. Escribí mi primer programa usando algo llamado "C ++" en 1985. Si quieres decir que esto no es C ++ "real", está bien, pero Antes de esto, estábamos escribiendo código y teníamos que obtener una clase de cadena de alguna parte. Una vez que tuvimos estas bases de código heredadas, no pudimos descartarlas exactamente o reescribirlas desde cero cuando obtuvimos un estándar. Ahora, lo que debería haber sucedido es que debería haber una clase de cadena que venía con cfront.
Gort the Robot
8
@DeadMG: si nadie usara un idioma hasta que tuviera el certificado ISO, entonces no se usaría ningún idioma, ya que nunca llegaría a ISO. No hay un estándar ISO para el ensamblador x86, pero estoy feliz de usar la plataforma
Martin Beckett
32

En realidad ... hay varios problemas con std::string, y sí, mejora un poco en C ++ 11, pero no nos adelantemos.

QStringy CStringson parte de antiguas bibliotecas, por lo tanto, existían antes de que C ++ se estandarizara (al igual que el SGI STL). Por lo tanto, tuvieron que crear una clase.

fbstringabordar preocupaciones de rendimiento muy específicas. El estándar prescribe una interfaz y la complejidad algorítmica garantiza mínimos, sin embargo, es una calidad de detalles de implementación si esto termina siendo rápido o no. fbstringtiene optimizaciones específicas (relacionadas con el almacenamiento, o más rápido, findpor ejemplo).

Otras preocupaciones que no fueron evocadas aquí (en vrac):

  • en C ++ 03 no es obligatorio que el almacenamiento sea contiguo, lo que hace que la interoperabilidad con C sea potencialmente difícil. C ++ 11 corrige esto.
  • std::string está codificando sin darse cuenta, y no tiene un código especial para UTF-8, es fácil almacenar una cadena UTF-8 y corromperla inadvertidamente
  • std::stringla interfaz está hinchada , muchos métodos podrían haberse implementado como funciones libres y muchos están duplicados para conformar tanto una interfaz basada en índice como una interfaz basada en iterador.
Matthieu M.
fuente
55
Re preocupación # 1 - C ++ 03 21.3.6 / 1 garantiza que c_str()devuelve un puntero al almacenamiento contiguo, lo que proporciona cierta interoperabilidad en C. Sin embargo, no puede modificar los datos apuntados. Las soluciones típicas incluyen el uso de a vector<char>.
John Dibling
@JohnDibling: Sí, y hay otra limitación: podría incurrir en una copia en el almacenamiento recientemente asignado (el Estándar no dice que no lo hará). Por supuesto, C ++ 11 tampoco impide la copia, pero como puedes hacerlo simplemente &s[0]ya no importa :)
Matthieu M.
1
@MatthieuM .: El puntero obtenido a través de &s[0]puede no apuntar a una cadena terminada en NUL (a menos que c_str()se haya llamado desde la última modificación).
Ben Voigt
2
@ Matthieu: no se permite otro búfer. " c_str()Devuelve: Un puntero ptal que p + i == &operator[](i)para cada ien [0,size()]".
Ben Voigt
3
Lo que también vale la pena señalar es que nadie en su sano juicio ya usa MFC, por lo que es difícil argumentar que CString es una clase de cadena en C ++ moderno.
DeadMG
7

Además de las razones publicadas aquí, también hay otra: la compatibilidad binaria . Los escritores de las bibliotecas no tienen control sobre qué std::stringimplementación está utilizando y si tiene el mismo diseño de memoria que el suyo.

std::stringes una plantilla, por lo que su implementación se toma de sus encabezados STL locales. Ahora imagine que está utilizando localmente alguna versión STL de rendimiento optimizado, totalmente compatible con el estándar. Por ejemplo, puede haber optado por introducir intrusiones en el búfer estático en cada uno std::stringpara reducir el número de asignaciones dinámicas y errores de caché. Como resultado, el diseño de la memoria y / o el tamaño de su implementación es diferente al de la biblioteca.

Si solo el diseño es diferente, algunas std::stringllamadas de función de miembro en instancias pasadas de la biblioteca al cliente o al revés pueden fallar, dependiendo de qué miembros se desplazaron.

Si el tamaño también es diferente, todos los tipos de biblioteca que tienen std::stringmiembro parecerán tener un tamaño diferente cuando se verifican en la biblioteca y en el código del cliente. Los miembros de datos que siguen al std::stringmiembro también tendrán desplazamientos desplazados, y cualquier acceso directo / acceso en línea llamado desde el cliente devolverá basura, a pesar de "estar bien" al depurar la propia biblioteca.

En pocas palabras: si la biblioteca y el código del cliente se compilan contra diferentes std::stringversiones, se vincularán perfectamente, pero puede dar lugar a algunos errores desagradables y difíciles de entender. Si cambia su std::stringimplementación, todas las bibliotecas que exponen miembros de STL deben volver a compilarse para que coincidan con el std::stringdiseño del cliente . Y debido a que los programadores quieren que sus bibliotecas sean robustas, rara vez se verá std::stringexpuesto en alguna parte.

Para ser justos, esto se aplica a todos los tipos de STL. IIRC no tienen un diseño de memoria estandarizado.

gwiazdorrr
fuente
2
Debes ser un programador * nix. La compatibilidad binaria de C ++ no es igual en todas las plataformas, y específicamente en Windows NO las clases que contienen miembros de datos son portables entre compiladores.
Ben Voigt
(Quiero decir, excepto los tipos de POD, e incluso entonces se necesitan requisitos de embalaje explícitos)
Ben Voigt
1
Gracias por el aporte, aunque no estoy hablando de un compilador diferente, estoy hablando de STL diferente.
gwiazdorrr
1
+1: ABI es una gran razón para lanzar su propia versión de una clase proporcionada por el compilador. Solo por eso, desearía que esta fuera la respuesta aceptada.
Thomas Eding
6

Hay muchas respuestas a la pregunta, pero aquí hay algunas:

  1. Legado. Muchas bibliotecas y clases de cadenas se escribieron ANTES de la existencia de std :: string.

  2. Por compatibilidad con el código en C. La biblioteca std :: string es C ++ donde hay otras bibliotecas de cadenas que funcionan con C y C ++.

  3. Para evitar asignaciones dinámicas. La biblioteca std :: string utiliza asignación dinámica y puede no ser adecuada para sistemas integrados, interrupción o código relacionado en tiempo real, o para funcionalidad de bajo nivel.

  4. Plantillas. La biblioteca std :: string se basa en plantillas. Hasta hace poco, varios compiladores de C ++ tenían un rendimiento de plantilla deficiente o incluso defectuoso. Desafortunadamente, trabajo en una industria que usa muchas herramientas personalizadas y una de nuestras cadenas de herramientas de un jugador importante en la industria no admite "oficialmente" el 100% de C ++ (con elementos defectuosos que son plantillas y otros).

Probablemente hay muchas más razones válidas también.

Adisak
fuente
2
"Bastante recientemente", que significa "Ha pasado una década desde que incluso Visual Studio tuvo un apoyo bastante razonable para ellos".
DeadMG
@DeadMG: Visual Studio no es el único compilador no compatible del mundo. Trabajo en videojuegos y a menudo trabajamos en compiladores personalizados para plataformas de hardware inéditas (sucede cada pocos años en los ciclos de la consola o cuando aparece un nuevo hardware). "Bastante recientemente" significa hoy: en este momento, ciertos compiladores no son compatibles con las plantillas. No puedo ser específico sin violar los NDA, pero actualmente estoy trabajando en una plataforma con cadenas de herramientas personalizadas donde el soporte de C ++, especialmente el cumplimiento de plantillas, se considera "experimental".
Adisak
4

Se trata principalmente de Unicode. El soporte estándar para Unicode es abismal en el mejor de los casos, y todos tienen sus propias necesidades de Unicode. Por ejemplo, ICU admite todas las funcionalidades de Unicode que pueda desear, detrás de la interfaz más desagradable generada automáticamente desde Java que pueda imaginar, y si está en Unix atascado con UTF-16 puede que no sea su idea de un buen momento.

Además, muchas personas necesitan diferentes niveles de soporte Unicode, no todos necesitan las complejas API de diseño de texto y esas cosas. Por lo tanto, es fácil ver por qué existen numerosas clases de cadenas: la estándar es bastante mala y todos tienen diferentes necesidades que las nuevas, sin que nadie logre crear una sola clase que pueda realizar muchas plataformas multiplataforma de soporte Unicode con una interfaz agradable.

En mi opinión, esto es principalmente culpa del Comité C ++ por no proporcionar correctamente el soporte para Unicode- en 1998 o 2003, tal vez fue comprensible, pero no en C ++ 11. Esperemos que en C ++ 17 les vaya mejor.

DeadMG
fuente
Hola, C ++ 20 aquí, ¿adivina qué pasó con el soporte Unicode?
Pasador antes del
-4

Esto se debe a que cada programador tiene algo que demostrar y siente la necesidad de crear su propia clase de cadena más rápida e increíble para su única función. Por lo general, es un poco superfluo y, en mi experiencia, conduce a todo tipo de conversiones de cadenas adicionales.

Chad Stewart
fuente
77
Si esto fuera cierto, esperaría ver un número similar de implementaciones de cadenas en lenguajes como Java, donde una buena implementación ha estado disponible todo el tiempo.
Bill K
@BillK, la cadena de Java es final, por lo que debe colocar una nueva funcionalidad en otro lugar.
Y mi punto es que, incluso siendo definitivo, en 20 años nunca he visto a nadie escribir una impelementación de cadena personalizada (Bueno, intenté mejorar el rendimiento de la concatenación de cadenas, pero resulta que Java es MUCHO más inteligente en cadena + cadena que tú ' d imagine)
Bill K
2
@Bill: Eso podría tener que ver con una cultura diferente. C ++ atrae a aquellos que quieren entender los detalles de bajo nivel. Java atrae a aquellos que solo quieren hacer el trabajo utilizando los componentes básicos de otra persona. (Tenga en cuenta que esto no es una declaración sobre cualquier individuo específico que elija usar cualquiera de los idiomas, sino sobre los objetivos de diseño y cultura respectivos de los idiomas)
Ben Voigt