Почему классы std::fstream не используют std::string?

На самом деле это не вопрос дизайна, хотя может показаться, что это так. (Ну да ладно, это вопрос дизайна). Мне интересно, почему классы С++ std::fstream не принимают std::string в своих конструкторах или открытых методах. Все любят примеры кода, поэтому:

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::string filename = "testfile";      
    std::ifstream fin;

    fin.open(filename.c_str()); // Works just fine.
    fin.close();

    //fin.open(filename); // Error: no such method.
    //fin.close();
}

Это достает меня все время при работе с файлами. Конечно, библиотека C++ будет использовать std::string везде, где это возможно?


person Bernard    schedule 28.08.2008    source источник


Ответы (10)


Взяв строку C, класс C++03 std::fstream уменьшил зависимость от std::string класс. Однако в C++11 класс std::fstream позволяет передавать std::string в качестве параметра конструктора.

Теперь вы можете задаться вопросом, почему нет прозрачного преобразования из строки std:string в строку C, поэтому класс, который ожидает строку C, все еще может принимать std::string, точно так же, как класс, который ожидает std::string, может принимать строку C.

Причина в том, что это вызовет цикл преобразования, что, в свою очередь, может привести к проблемам. Например, предположим, что std::string можно преобразовать в строку C, чтобы вы могли использовать std::strings с fstreams. Предположим также, что строка C может быть преобразована в std::strings, как это указано в текущем стандарте. Теперь рассмотрим следующее:

void f(std::string str1, std::string str2);
void f(char* cstr1, char* cstr2);

void g()
{
    char* cstr = "abc";
    std::string str = "def";
    f(cstr, str);  // ERROR:  ambiguous
}

Поскольку вы можете преобразовать строку std::string в строку C любым способом, вызов f() может разрешить любую из двух альтернатив f() и, таким образом, неоднозначен. Решение состоит в том, чтобы разорвать цикл преобразования, сделав одно направление преобразования явным, что STL решила сделать с c_str().

person wilhelmtell    schedule 01.09.2008
comment
Образец не должен вызывать двусмысленности с правилами разрешения перегрузки. f(char*,std::string) – это точное совпадение, в то время как для других потребуется преобразование, следовательно, первая будет наилучшей жизнеспособной функцией. Если вы удалили первый f, то as (char*&,std::string&) -> (char*,std::string) -> (char*,char*) представляет собой лучшая последовательность преобразования, чем (char*&,std::string&) -> (char*,std: :string) -> (std::string,char*), вторая f была бы лучшей жизнеспособной функцией. Я что-то упускаю? - person outis; 15.11.2010
comment
Эта микрооптимизация действительно идиотская, так как (1) строки тесно связаны с любой конструкцией языка, особенно со строками (это похоже на высказывание «я не хочу, чтобы GMP зависело от шорт») и (2) char* это зло, вы не должны поощрять пользователей использовать его, если им это не нужно, и миллисекунды во время компиляции того стоят, наконец (3) они могли бы определить <stringfwd> и использовать строку, не завися от нее, поэтому я утверждают, что развязка зависимостей не является веской причиной для этого случая. - person Elazar Leibovich; 05.04.2011
comment
Причина явного приведения типов с использованием c_str() по сравнению с неявным преобразованием типов с использованием конструктора преобразования заключается в том, что для строки c требуется дополнительный \0, поэтому std::string не может вернуть указатель const char* на свое внутреннее представление, и это приведет к снижению производительности. Используя явное приведение типов, программист может решить гораздо лучше. - person Micha Wiedenmann; 12.11.2012

Есть несколько мест, где комитет по стандартизации C++ не оптимизировал взаимодействие между средствами в стандартной библиотеке.

std::string и его использование в библиотеке — одно из них.

Еще один пример — std::swap. Многие контейнеры имеют функцию-член swap, но перегрузка std::swap не предоставляется. То же самое касается std::sort.

Надеюсь, все эти мелочи будут исправлены в грядущем стандарте.

person Christopher    schedule 28.08.2008

Может быть, это утешение: все fstream получили open(string const &,...) рядом с open(char const *,...) в рабочем проекте стандарта C++0x. (см., например, 27.8.1.6 для объявления basic_ifstream)

Так что, когда он будет доработан и реализован, он вас больше не достанет :)

person Pieter    schedule 15.09.2008

Библиотека потокового ввода-вывода была добавлена ​​в стандартную библиотеку C++ до STL. Чтобы не нарушать обратную совместимость, было решено не изменять библиотеку ввода-вывода при добавлении STL, даже если это означало некоторые проблемы, подобные той, которую вы поднимаете.

person Drealmer    schedule 16.09.2008

@ Bernard:
Монолиты "без струн". «Все за одного и один за всех» может работать для мушкетеров, но не так хорошо для дизайнеров классов. Вот пример, который не совсем образцовый, и он иллюстрирует, насколько сильно вы можете ошибиться, когда дизайн превращается в перепроектирование. Пример, к сожалению, взят из ближайшей стандартной библиотеки... ~ http://www.gotw.ca/gotw/084.htm

person swmc    schedule 15.09.2008

Это несерьезно, это правда. Что вы подразумеваете под большим интерфейсом std::string? Что означает большой в этом контексте - много вызовов методов? Я не шучу, мне действительно интересно.

У него больше методов, чем ему действительно нужно, и его поведение с использованием целочисленных смещений, а не итераторов, немного сомнительно (поскольку это противоречит тому, как работает остальная часть библиотеки).

Реальная проблема, я думаю, заключается в том, что библиотека C++ состоит из трех частей; у него есть старая библиотека C, у него есть STL, и у него есть строки и потоки ввода-вывода. Хотя были предприняты некоторые усилия для объединения различных частей (например, добавление перегрузок в библиотеку C, поскольку C++ поддерживает перегрузку; добавление итераторов к basic_string; добавление адаптеров итераторов iostream), при работе возникает много несоответствий. посмотрите на деталь.

Например, basic_string включает в себя методы, которые являются ненужными дубликатами стандартных алгоритмов; различные методы поиска, вероятно, можно было бы безопасно удалить. Другой пример: локали используют необработанные указатели вместо итераторов.

person DrPizza    schedule 28.08.2008

C++ вырос на машинах меньшего размера, чем монстры, для которых мы пишем код сегодня. Когда iostream был новым, многие разработчики действительно заботились о размере кода (им приходилось умещать всю свою программу и данные в несколько сотен КБ). Поэтому многие не хотели тянуть "большую" библиотеку строк C++. Многие даже не использовали библиотеку iostream по тем же причинам, из-за размера кода.

У нас не было тысяч мегабайт ОЗУ, которые можно было бы разбрасывать, как сегодня. Обычно у нас не было связывания на функциональном уровне, поэтому мы были во власти разработчика библиотеки, чтобы использовать множество отдельных объектных файлов или вытягивать тонны неиспользуемого кода. Все эти FUD заставили разработчиков отказаться от std::string.

Тогда я тоже избегал std::string. «Слишком раздутый», «слишком часто вызываемый malloc» и т. д. Глупое использование буферов на основе стека для строк, а затем добавление всевозможного утомительного кода, чтобы убедиться, что он не переполняется.

person doug65536    schedule 10.11.2012

Есть ли какой-либо класс в STL, который принимает строку... Я так не думаю (в моем быстром поиске я не нашел ни одного). Так что, вероятно, это какое-то дизайнерское решение, что ни один класс в STL не должен зависеть от любого другого класса STL (который напрямую не нужен для функциональности).

person Magnus Westin    schedule 28.08.2008

Я считаю, что об этом подумали и сделали, чтобы избежать зависимости; т. е. #include ‹fstream› не должен заставлять #include ‹string›.

Честно говоря, это кажется совершенно несущественной проблемой. Лучше спросить, почему интерфейс std::string такой большой?

person DrPizza    schedule 28.08.2008

В настоящее время вы можете решить эту проблему очень легко: добавьте -std=c++11 к вашему CFLAGS.

person Jonathan Birge    schedule 10.03.2016