Por que existem tantas classes de strings em face de std :: string?

56

Parece-me que muitas bibliotecas C ++ maiores acabam criando seu próprio tipo de string. No código do cliente você tem que usar o que a partir da biblioteca ( QString, CString, fbstringetc., tenho certeza que qualquer um pode citar alguns) ou manter a conversão entre o tipo padrão e aquela que os usos de biblioteca (que na maioria das vezes envolve pelo menos uma cópia).

Então, há uma característica específica errada ou algo errado std::string(assim como a auto_ptrsemântica era ruim)? Isso mudou no C ++ 11?

Tamás Szelei
fonte
32
É chamado de "síndrome não inventada aqui".
Cat Plus Plus
10
@CatPlusPlus QString e CString antecederam std :: string.
Gort the Robot
8
@ Cat Plus Plus: Esta síndrome não parece afetar a classe Java String.
Giorgio
20
@Giorgio: programadores Java estão muito ocupados inventando soluções alternativas para que as deficiências de linguagem se preocupem com as classes de strings (a propósito, o Android reinventou o String).
Cat Plus Plus
9
@Giorgio: Provavelmente porque o suporte sintático codificado do Java para java.lang.String(falta de sobrecarga do operador etc.) dificultaria o uso de qualquer outra coisa.
Caracol mecânico

Respostas:

57

A maioria dessas bibliotecas C ++ maiores foi iniciada antes de std::stringser padronizada. Outros incluem recursos adicionais padronizados tardiamente, ou ainda não padronizados, como suporte para UTF-8 e conversão entre codificações.

Se essas bibliotecas fossem implementadas hoje, provavelmente escolheriam escrever funções e iteradores que operam em std::stringinstâncias.

Ben Voigt
fonte
5
O suporte ao UTF-8 é padronizado desde o C ++ 98. Em um inconveniente tal e parcialmente implementação definido maneira que cerca de ninguém parece ser capaz de usá-lo
AProgrammer
9
@ AProgrammer: charé garantido que é grande o suficiente para armazenar qualquer ponto de código UTF-8. AFAIK, esse é o único "suporte" fornecido pelo C ++ 98.
Ben Voigt
4
@ AProgrammer: Esse suporte é realmente bastante inútil.
DeadMG
4
@ AProgrammer Esse código de idioma provavelmente está quebrado, pois nãowchar_t é grande o suficiente para representar todos os pontos de código Unicode. Além disso, houve toda essa discussão sobre UTF-16 considerado nocivo onde o argumento muito convincente foi feito que UTF-8 deve ser usado exclusivamente ...
Konrad Rudolph
6
@KonradRudolph, não é o sistema de localidade que está quebrado lá (a definição de wchar_t é "ampla o suficiente para qualquer conjunto de caracteres suportado"); os sistemas comprometidos com um wchar_t de 16 bits se comprometeram ao mesmo tempo em não dar suporte ao Unicode. Bem, o culpado é o Unicode, que primeiro garantiu que nunca usaria pontos de código que precisassem de mais de 16 bits, depois sistemas comprometidos com um wchar_t de 16 bits e, em seguida, comutação unicode para precisar de mais de 16 bits.
AProgrammer
39

String é o grande embaraço de C ++.

Nos primeiros 15 anos, você não fornece uma classe de string - forçando todos os compiladores em todas as plataformas e todos os usuários a criarem seus próprios.

Então você cria algo confuso sobre se é uma API de manipulação de cadeia completa ou apenas um contêiner de char STL, com alguns algoritmos que duplicam os de um std :: Vector ou são diferentes.

Onde uma operação óbvia de strings como replace () ou mid () envolve uma confusão de iteradores, é necessário introduzir uma nova palavra-chave 'auto' para manter a declaração adequada em uma única página e levar a maioria das pessoas a desistir de todo o idioma .

E então você tem o 'suporte' unicode e o std :: wstring que é simplesmente árduo ...

<rant off> obrigado - estou me sentindo muito melhor agora.

Martin Beckett
fonte
12
@DeadMG - sim, e foi padronizado em 1998, 15 anos após a invenção e 6 anos após o uso da MSFT. Sim, os iteradores são uma maneira útil de fazer com que uma matriz e lista pareçam iguais. Você acha que elas são uma maneira óbvia de manipular seqüências de caracteres?
Martin Beckett
3
C com Classes foi inventado em 1983. Não em C ++. As únicas bibliotecas padrão são aquelas determinadas pelo padrão - o que, estranhamente, só pode acontecer quando você tiver um padrão; portanto, a data mais antiga possível para qualquer biblioteca padrão é 1998. E os iteradores podem ser considerados exatamente iguais aos índices, mas com forte tipagem. Eu sou a favor do fato de que os iteradores são ruins em comparação com os intervalos, mas isso não é realmente específico std::string. A falta de uma classe String em 1983 não justifica ter mais agora.
DeadMG
8
Pensei iostreams eram grandes constrangimento C ++ s ...
Doug T.
18
@DeadMG As pessoas estavam usando algo chamado "C ++" por muitos anos antes de 1998. Eu escrevi meu primeiro programa usando algo chamado "C ++" em 1985. Se você quer dizer que este não é C ++ "real", tudo bem, mas antes disso, estávamos escrevendo código e tivemos que obter uma classe de string de algum lugar. Depois que tínhamos essas bases de código herdadas, não podíamos exatamente jogá-las fora ou reescrevê-las do zero quando obtivemos um padrão. Agora, o que deveria ter acontecido é que deveria haver uma classe de strings que acompanha o cfront.
Gort the Robot
8
@DeadMG - Se ninguém usasse um idioma até ter o certificado ISO, nenhum idioma seria usado, pois nunca chegaria ao ISO. Não há um padrão ISO para assembler x86, mas estou feliz de usar a plataforma
Martin Beckett
32

Na verdade ... existem vários problemas e std::string, sim, fica um pouco melhor no C ++ 11, mas não vamos nos antecipar.

QStringe CStringfazem parte de bibliotecas antigas ; portanto, elas existiam antes da padronização do C ++ (assim como o SGI STL). Eles tiveram que criar uma classe.

fbstringabordar preocupações de desempenho muito específicas. A Norma prescreve uma interface e a complexidade algorítmica garante mínimos, no entanto, é um detalhe de Qualidade de Implementação, se isso acaba sendo rápido ou não. fbstringpossui otimizações específicas (relacionadas ao armazenamento ou mais rápidas, findpor exemplo).

Outras preocupações que não foram evocadas aqui (en vrac):

  • no C ++ 03, não é obrigatório que o armazenamento seja contíguo, dificultando a interoperabilidade com o C. O C ++ 11 corrige isso.
  • std::string está codificando inconscientemente e não possui um código especial para UTF-8, é fácil armazenar uma string UTF-8 nela e corrompê-la inadvertidamente
  • std::stringSe a interface estiver inchada , muitos métodos poderiam ter sido implementados como funções livres e muitos são duplicados para se conformar tanto a uma interface baseada em índice quanto a uma interface baseada em iterador.
Matthieu M.
fonte
5
A preocupação # 1 - C ++ 03 21.3.6 / 1 garante que c_str()retorne um ponteiro para o armazenamento contíguo, o que fornece alguma interoperabilidade em C. No entanto, você não pode modificar os dados apontados. Soluções alternativas típicas incluem o uso de um vector<char>.
precisa saber é o seguinte
@JohnDibling: Sim, e há outra limitação: pode haver uma cópia no armazenamento alocado recentemente (o Padrão não diz que não deve). Claro C ++ 11 não impede a cópia de qualquer um, mas já que você pode simplesmente fazer &s[0]isso não importa mais tempo :)
Matthieu M.
11
@MatthieuM .: O ponteiro obtido via &s[0]pode não apontar para uma string terminada em NUL (a menos que c_str()tenha sido chamada desde a última modificação).
precisa
2
@ Matthieu: Outro buffer não é permitido. " c_str()Returns: Um ponteiro pde tal forma que p + i == &operator[](i)para cada iem [0,size()]".
precisa
3
O que também vale a pena notar é que ninguém em sã consciência usa mais o MFC, por isso é difícil argumentar que o CString é uma classe de string no C ++ moderno.
DeadMG
7

Além das razões postadas aqui, há também outra - a compatibilidade binária . Os escritores das bibliotecas não têm controle sobre qual std::stringimplementação você está usando e se ela possui o mesmo layout de memória que o deles.

std::stringé um modelo, portanto, sua implementação é obtida nos cabeçalhos STL locais. Agora imagine que você está usando localmente alguma versão STL com desempenho otimizado, totalmente compatível com o padrão. Por exemplo, você pode optar por invadir o buffer estático em cada um std::stringpara reduzir o número de alocações dinâmicas e falhas de cache. Como resultado, o layout da memória e / ou o tamanho da sua implementação são diferentes dos da biblioteca.

Se apenas o layout for diferente, algumas std::stringfunções de membro chamam instâncias transmitidas da biblioteca para o cliente ou o contrário pode falhar, dependendo de quais membros foram deslocados.

Se o tamanho também for diferente, todos os tipos de bibliotecas que possuem std::stringmembros parecerão ter diferentes sizeof quando marcados na biblioteca e no código do cliente. Os membros de dados após o std::stringmembro também terão os deslocamentos deslocados, e qualquer acesso direto / acessador em linha chamado do cliente retornará lixo, apesar de "parecer bem" ao depurar a própria biblioteca.

Bottomline - se a biblioteca e o código do cliente forem compilados em std::stringversões diferentes , eles serão vinculados muito bem, mas isso pode resultar em alguns erros desagradáveis ​​e difíceis de entender. Se você alterar sua std::stringimplementação, todas as bibliotecas que expõem membros do STL deverão ser recompiladas para corresponder ao std::stringlayout do cliente . E como os programadores desejam que suas bibliotecas sejam robustas, você raramente será std::stringexposto em qualquer lugar.

Para ser justo, isso se aplica a todos os tipos de STL. IIRC eles não têm layout de memória padronizado.

gwiazdorrr
fonte
2
Você deve ser um programador * nix. A compatibilidade binária do C ++ não é igual em todas as plataformas e, especificamente, no Windows, as classes que contêm membros de dados são portáveis ​​entre compiladores.
Ben Voigt
(Quero dizer, exceto tipos POD, e são necessários requisitos de embalagem, mesmo assim explícitas)
Ben Voigt
11
Obrigado pela contribuição, embora não esteja falando de um compilador diferente, estou falando de um STL diferente.
precisa saber é o seguinte
11
+1: A ABI é um grande motivo para lançar sua própria versão de uma classe fornecida pelo compilador. Só por isso, eu gostaria que essa fosse a resposta aceita.
Thomas Eding
6

Existem muitas respostas para a pergunta, mas aqui estão algumas:

  1. Legado. Muitas bibliotecas e classes de strings foram escritas ANTES da existência de std :: string.

  2. Para compatibilidade com o código em C. A biblioteca std :: string é C ++, onde há outras bibliotecas de strings que funcionam com C e C ++.

  3. Para evitar alocações dinâmicas. A biblioteca std :: string usa alocação dinâmica e pode não ser adequada para sistemas incorporados, interrupção ou código relacionado em tempo real ou para funcionalidade de baixo nível.

  4. Modelos. A biblioteca std :: string é baseada em modelos. Até bem recentemente, vários compiladores C ++ tinham suporte de modelo com desempenho insuficiente ou até com erros. Infelizmente, eu trabalho em um setor que usa muitas ferramentas personalizadas e uma de nossas cadeias de ferramentas de um dos principais players do setor não "oficialmente" 100% suporta C ++ (com itens de buggy sendo modelos e outros).

Provavelmente também existem muitos outros motivos válidos.

Adisak
fonte
2
"Recentemente" significa "Faz uma década que até o Visual Studio tinha um suporte bastante razoável para eles"?
DeadMG
@DeadMG - O Visual Studio não é o único compilador não compatível no mundo. Eu trabalho em videogames e geralmente trabalhamos em compiladores personalizados para plataformas de hardware não lançadas (acontece a cada poucos anos nos ciclos do console ou quando um novo hardware aparece). "Recentemente" significa hoje - neste momento, certos compiladores não suportam modelos bem. Não posso ser específico sem violar os NDAs, mas atualmente estou trabalhando em uma plataforma com cadeias de ferramentas personalizadas nas quais o suporte a C ++ - especialmente a conformidade com modelos - é considerado "experimental".
Adisak
4

É principalmente sobre Unicode. O suporte padrão para Unicode é péssimo, e todos têm suas próprias necessidades Unicode. Por exemplo, o ICU suporta todas as funcionalidades Unicode que você poderia desejar, por trás da interface gerada automaticamente a partir de Java mais repugnante que você possa imaginar, e se você estiver no Unix bloqueado com UTF-16, pode não ser sua ideia de um bom tempo.

Além disso, muitas pessoas precisam de níveis diferentes de suporte a Unicode - nem todo mundo precisa das APIs complexas de layout de texto e coisas assim. Portanto, é fácil ver por que existem várias classes de strings - a Standard é uma droga e todos têm necessidades diferentes das novas, sem que ninguém consiga criar uma única classe que possa executar várias plataformas cruzadas de suporte Unicode com uma interface agradável.

Na minha opinião, isso é principalmente culpa do Comitê C ++ por não fornecer corretamente suporte ao Unicode em 1998 ou 2003, talvez fosse compreensível, mas não no C ++ 11. Felizmente, em C ++ 17, eles farão melhor.

DeadMG
fonte
Olá, C ++ 20 aqui, adivinhe o que aconteceu com o suporte a Unicode?
Passer Por
-4

É porque todo programador tem algo a provar e sente a necessidade de criar sua própria classe de strings mais rápida e impressionante para sua única função. Geralmente é um pouco supérfluo e leva a todos os tipos de conversões extras de strings na minha experiência.

Chad Stewart
fonte
7
Se isso fosse verdade, eu esperaria ver um número semelhante de implementações de String em linguagens como Java, onde uma boa implementação está disponível o tempo todo.
Bill K
@BillK, a Java String é final, então você precisa colocar novas funcionalidades em outro lugar.
E o que quero dizer é que, mesmo sendo final, em 20 anos nunca vi alguém escrever uma impelementação de string personalizada (bem, tentei melhorar o desempenho da concatenação de string, mas acontece que o java é MUITO mais inteligente em string + string do que você ' d imaginar)
Bill K
2
@ Bill: Isso pode ter a ver com uma cultura diferente. C ++ atrai aqueles que desejam entender os detalhes de baixo nível. Java atrai aqueles que querem apenas fazer o trabalho usando os blocos de construção de outra pessoa. (Note-se que esta não é uma declaração sobre qualquer escolha indivíduo específico para usar a linguagem, mas cerca respectivos objetivos do projeto dos idiomas e cultura)
Ben Voigt