Чому існує стільки класів рядків перед обличчям std :: string?


56

Мені здається, що багато більших бібліотек C ++ в кінцевому підсумку створюють власний тип рядка. У клієнтському коді вам потрібно використовувати той, який використовується з бібліотеки ( QString, CStringі fbstringт. Д., Я впевнений, що хтось може назвати їх декілька) або продовжувати конвертувати між типовим типом і тим, який використовує бібліотека (що більшість часу передбачає хоча б один примірник).

Отже, чи є певна невдача чи щось не так std::string(як і auto_ptrсемантика була поганою)? Чи змінилося це в C ++ 11?


32
Це називається "синдром не придуманого тут".
Cat Plus Plus

10
@CatPlusPlus QString та CString попередньо std :: string.
Gort the Robot

8
@Cat Plus Plus: Схоже, цей синдром не впливає на клас Java String.
Джорджіо

20
@Giorgio: Програмісти Java занадто зайняті, щоб вигадувати обхідні проблеми для мовних дефіцитів, щоб переживати про строкові класи (до речі, Android винаходив String).
Cat Plus Plus

9
@Giorgio: Це, мабуть, тому, що сильно кодована синтаксична підтримка Java java.lang.String(відсутність перевантаження оператора тощо) заподіює біль використовувати будь-що інше.
Механічний равлик

Відповіді:


57

Більшість цих більших бібліотек C ++ були створені раніше, ніж std::stringбуло стандартизовано. Інші включають додаткові функції, які були стандартизовані пізно або ще не стандартизовані, такі як підтримка UTF-8 та перетворення між кодуванням.

Якби ці бібліотеки були реалізовані сьогодні, вони, ймовірно, вирішили записати функції та ітератори, які працюють над std::stringекземплярами.


5
Підтримка UTF-8 стандартизована з моменту C ++ 98. Таким незручним і частково визначеним способом реалізації, що майже ніхто, здається, не може ним користуватися
AProgrammer

9
@AProgrammer: charгарантовано буде достатньо великим, щоб вмістити будь-яку кодову точку UTF-8. AFAIK, це єдина "підтримка", яку надав C ++ 98.
Бен Войгт

4
@AProgrammer: Ця підтримка насправді зовсім марна.
DeadMG

4
@AProgrammer тієї місцевості, можливо , порушена , так як wchar_tце НЕ досить великий , щоб представляти все кодові точки Unicode. Крім того, ціла дискусія про UTF-16 вважалася шкідливою, коли був виправданий аргумент, що UTF-8 слід використовувати виключно
Конрад Рудольф

6
@KonradRudolph, це не локальна система, яка там порушена (визначення wchar_t "досить широке для будь-якого підтримуваного набору символів"); системи, які взяли на себе 16-бітовий wchar_t, водночас взяли на себе зобов’язання не підтримувати Unicode. Ну, винуватцем є Unicode, який спочатку гарантував, що ніколи не буде використовувати кодові точки, що потребують більше 16 біт, потім системи, що здійснюють 16-бітовий wchar_t, а потім для переключення unicode потрібно більше 16 біт.
AProgrammer

39

Рядок є великим збентеженням C ++.

Протягом перших 15 років ви взагалі не надаєте клас рядків - змушуючи кожного компілятора на кожній платформі та кожного користувача створювати свій власний.

Тоді ви робите щось заплутане в тому, чи повинен це бути повноцінний API маніпуляції з рядком або просто контейнер символів STL, з деякими алгоритмами, які дублюють ті, які знаходяться на std :: Vector, або різні.

Якщо очевидна струнна операція, як заміна () або середина (), передбачає такий безлад ітераторів, що вам потрібно ввести нове ключове слово "авто", щоб укладати вислів на одній сторінці і змушує більшість людей здаватися на всій мові .

І тоді у вас є "підтримка" Unicode і std :: wstring, що просто arghh .....

<rant off> дякую - зараз я відчуваю себе набагато краще.


12
@DeadMG - так, і це було стандартизовано в 1998 році, 15 років після його винайдення і 6 років після того, як навіть MSFT використовували його. Так, ітератори є корисним способом зробити масив і список виглядати однаково, чи вважаєте ви, що це очевидний спосіб зробити обробку рядків?
Мартін Бекетт

3
C з класами був винайдений у 1983 році. Не C ++. Єдині бібліотеки стандарту - це ті, які визначаються Standard-, що, як не дивно, може статися лише після того, як у вас є Standard, тому найрання можлива дата для будь-якої бібліотеки Standard - 1998. І ітератори можна вважати точно рівними індексами, але сильно набрані. Я все за те, що ітератори висмоктують порівняно з діапазонами, але це насправді не конкретно std::string. Відсутність класу String в 1983 році не виправдовує наявність їх більше.
DeadMG

8
Я подумав, що потоки є великим збентеженням C ++ ...
Дуг Т.

18
@DeadMG Люди використовували те, що називалося "C ++" протягом багатьох років до 1998 року. Я написав свою першу програму, використовуючи щось, що називалося "C ++" в 1985 році. Якщо ви хочете сказати, що це не "справжній" C ++, це добре, але до цього ми писали код і мусили звідкись отримати клас струн. Коли ми мали ці застарілі бази даних, ми не могли їх точно викинути або переписати з нуля, коли отримали стандарт. Тепер, що мало статися, це те, що мав бути клас струн, який прийшов із cfront.
Gort the Robot

8
@DeadMG - Якщо ніхто не використовував мову, поки вона не мала ISO cert, то жодна мова ніколи не буде використовуватися, оскільки вона ніколи не дістанеться до ISO. Не існує стандарту ISO для асемблера x86, але я радий використовувати платформу
Martin Beckett

32

Насправді ... Є кілька проблем std::string, і так, це стає трохи краще в C ++ 11, але не давайте випереджати себе.

QStringі CStringє частиною старих бібліотек, тому вони існували до стандартизації C ++ (приблизно як SGI STL). Таким чином, їм довелося створити клас.

fbstringвирішити дуже конкретні проблеми щодо ефективності. Стандарт прописує інтерфейс, а алгоритмічна складність гарантує мінімуми, однак це деталі якості впровадження, незалежно від того, швидше це чи ні. fbstringмає специфічні оптимізації (наприклад, пов'язані з зберіганням даних або швидше find).

Інші проблеми, які тут не викликали (en vrac):

  • в C ++ 03 не обов'язково зберігання зберігатись безперервно, що робить інтероперабельність із C потенційно складною. C ++ 11 виправляє це.
  • std::string кодує невідомо і не має спеціального коду для UTF-8, легко зберігати в ньому рядок UTF-8 і ненавмисно пошкоджувати його
  • std::stringінтерфейс роздутий , багато методів могли бути реалізовані як вільні функції, а багато дублюються, щоб відповідати як інтерфейсу на основі індексу, так і інтерфейсу на основі ітератора.

5
Стурбованість №1 - C ++ 03 21.3.6 / 1 гарантує c_str()повернення покажчика до суміжного сховища, що забезпечує деяку C-сумісність. Однак ви не можете змінювати дані, що вказуються. Типові способи вирішення включають використання vector<char>.
Джон Дайлінг

@JohnDibling: Так, є й інше обмеження: воно може містити копію в щойно виділеному сховищі (Стандарт не говорить про те, що не повинен). Звичайно, C ++ 11 також не заважає копіювати, але оскільки ви просто можете &s[0]це зробити, це вже не має значення :)
Матьє М.

1
@MatthieuM. Вказівник, отриманий через, &s[0]не може вказувати на рядок, що закінчується NUL (якщо c_str()не викликався з моменту останньої модифікації).
Бен Войгт

2
@Matthieu: Інший буфер заборонений. " c_str()Повертає: вказівник pтакий, що p + i == &operator[](i)для кожного iз [0,size()]".
Ben Voigt

3
Що також варто зазначити, що ніхто з розумом більше не використовує MFC, тому важко стверджувати, що CString - це рядковий клас у сучасному C ++.
DeadMG

7

Окрім наведених тут причин, існує ще одна - бінарна сумісність . Письменники бібліотек не контролюють, яку std::stringреалізацію ви використовуєте та чи має вона такий же макет пам'яті, що і їх.

std::stringє шаблоном, тому його реалізація взята з локальних заголовків STL. Тепер уявіть, що ви локально використовуєте оптимізовану для продуктивності версію STL, повністю сумісну зі стандартом. Наприклад, ви, можливо, вибрали втручання статичного буфера в кожен, std::stringщоб зменшити кількість динамічних виділень і пропусків кешу. Як результат, схема пам'яті та / або розмір вашої реалізації відрізняється від бібліотеки.

Якщо лише макет відрізняється, деякі std::stringвиклики функцій учасників для екземплярів, переданих з бібліотеки до клієнта, або навпаки можуть не працювати, залежно від того, які члени були зміщені.

Якщо розмір також різний, всі типи бібліотек, які мають std::stringчлен, виявляться різними розмірами при позначці в бібліотеці та в коді клієнта. У членів даних, які перебувають у наступному std::stringучаснику, також буде змінено зсув, і будь-який прямий доступ / вбудований доступ, викликаний від клієнта, поверне сміття, незважаючи на те, що "виглядає нормально" під час налагодження самої бібліотеки.

Підсумок - якщо бібліотека та код клієнта зібрані у різних std::stringверсіях, вони будуть посилатися чудово, але це може призвести до деяких неприємних, важких для розуміння помилок. Якщо ви зміните свою std::stringреалізацію, всі бібліотеки, що відкривають членів STL, повинні бути перекомпільовані, щоб відповідати std::stringмакеті клієнта . А оскільки програмісти хочуть, щоб їх бібліотеки були надійними, їх рідко можна побачити std::stringбудь-де.

Справедливості це стосується всіх типів STL. IIRC вони не мають стандартизованого розміщення пам'яті.


2
Ви повинні бути програмістом * nix. Бінарна сумісність C ++ не однакова на всіх платформах, а конкретно для класів Windows NO, що містять дані, переносяться між компіляторами.
Бен Войгт

(Я маю на увазі, крім типів POD, і навіть тоді потрібні явні вимоги до упаковки)
Ben Voigt

1
Дякую за вклад, хоча я не кажу про різний компілятор, я кажу про різні STL.
gwiazdorrr

1
+1: ABI - це вагома причина для передачі власної версії класу, що постачається компілятором. Я тільки хотів, щоб це була прийнята відповідь.
Томас Едінг

6

Відповідей на це питання багато, але ось деякі:

  1. Спадщина. Багато бібліотеки рядків і класів були написані ПРІОР до існування std :: string.

  2. Для сумісності з кодом у C. Бібліотека std :: string - це C ++, де є інші бібліотеки рядків, які працюють з C і C ++.

  3. Щоб уникнути динамічних виділень. Бібліотека std :: string використовує динамічне розподіл і може не підходити для вбудованих систем, коду, що переривається або в режимі реального часу, або для функцій низького рівня.

  4. Шаблони. Бібліотека std :: string заснована на шаблонах. Ще зовсім недавно у деяких компіляторах C ++ була недостатньо ефективна або навіть помилкова підтримка шаблонів. На жаль, я працюю в галузі, яка використовує безліч спеціальних інструментів, і один з наших інструментальних ланцюжків від головного гравця в галузі не "офіційно" підтримує 100% C ++ (з баггі, як шаблони та ін.).

Напевно, є й багато поважних причин.


2
"Досить недавно", що означає "Минуло десятиліття, коли навіть Visual Studio мала досить розумну підтримку для них"?
DeadMG

@DeadMG - Visual Studio - не єдиний невідповідний компілятор у світі. Я працюю у відеоіграх, і ми часто працюємо над спеціальними компіляторами для невипущених апаратних платформ (трапляється кожні кілька років у консольних циклах або як з’являється нове обладнання). "Досить недавно" означає сьогодні - Зараз певні компілятори не підтримують шаблони добре. Я не можу бути конкретним, не порушуючи NDA, але зараз я працюю на платформі зі спеціальними ланцюжками інструментів, де підтримка C ++ - особливо відповідність шаблону - вважається "експериментальною".
Адісак

4

Йдеться переважно про Unicode. Стандартна підтримка Unicode в кращому випадку не відповідає нормам, і кожен має свої потреби в Unicode. Наприклад, ICU підтримує всі функції Unicode, які ви могли хотіти, за самим огидним автоматично створеним з Java інтерфейсом, який ви могли собі уявити, і якщо ви перебуваєте на Unix, що ви застрягли з UTF-16, цілком може бути не ваша ідея гарний час.

Крім того, багатьом людям потрібні різні рівні підтримки Unicode - не всім потрібні складні API компонування тексту та подібні речі. Тож легко зрозуміти, чому існують численні строкові класи - Стандартний досить непоганий, і всі мають різні потреби від нових, при цьому ніхто не встигає створити єдиний клас, який може виконувати безліч кросплатформних платформ Unicode із приємним інтерфейсом.

На мою думку, це здебільшого вина Комітету C ++ у тому, що він не надав належну підтримку Unicode - у 1998 або 2003 роках, можливо, це було зрозуміло, але це не було в C ++ 11. Сподіваємось, в C ++ 17 вони зробляться краще.


Здрастуйте, C ++ 20 тут, здогадайтесь, що сталося з підтримкою Unicode?
Перехожий

-4

Це тому, що кожен програміст має щось довести і відчуває необхідність створити власний дивовижний, швидший клас струн для своєї однієї, приголомшливої ​​функції. Зазвичай це трохи зайве і призводить до різного роду додаткових перетворень на моєму досвіді.


7
Чи було це правдою, я очікував би побачити подібну кількість реалізацій String на таких мовах, як Java, де хороша реалізація була доступна протягом усього часу.
Білл К

@BillK Рядок Java остаточний, тому вам доведеться розмістити нову функціональність в іншому місці.

І моя думка, навіть будучи остаточним, за 20 років я ніколи не бачив, щоб хто-небудь писав власну імпементацію рядків (Ну, я намагався покращити продуктивність конкатенації рядків, але, як виявляється, Java набагато розумніша на рядок + рядок, ніж ви ' буду уявити)
Білл К

2
@Bill: Це може мати відношення до іншої культури. C ++ приваблює тих, хто хоче зрозуміти деталі низького рівня. Java приваблює тих, хто просто хоче виконати роботу, використовуючи чужі будівельні блоки. (Зверніть увагу, що це не твердження про будь-яку конкретну особу, яка вирішила використовувати будь-яку мову, а про відповідні дизайнерські цілі та культуру мов)
Ben Voigt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.