Возврат ifstream в функцию

Вот, наверное, очень нубский вопрос для вас: как (если это вообще возможно) я могу вернуть ifstream из функции?

По сути, мне нужно получить имя файла базы данных от пользователя, и если база данных с таким именем файла не существует, мне нужно создать этот файл для пользователя. Я знаю, как это сделать, но только попросив пользователя перезапустить программу после создания файла. Я хотел по возможности избежать этого неудобства для пользователя, но приведенная ниже функция не компилируется в gcc:

ifstream getFile() {
    string fileName;
    cout << "Please enter in the name of the file you'd like to open: ";
    cin >> fileName;
    ifstream first(fileName.c_str());
    if(first.fail()) {
        cout << "File " << fileName << " not found.\n";
        first.close();
        ofstream second(fileName.c_str());
        cout << "File created.\n";
        second.close();
        ifstream third(fileName.c_str());
        return third; //compiler error here
    }
    else
        return first;
}

РЕДАКТИРОВАТЬ: извините, забыл сказать вам, где и в чем была ошибка компилятора:

main.cpp:45: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here 

РЕДАКТИРОВАТЬ: я изменил функцию, чтобы вместо этого возвращать указатель, как предложил Ремус, и изменил строку в main() на «ifstream database = *getFile()»; теперь я снова получаю эту ошибку, но на этот раз в строке main():

main.cpp:27: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here

person wrongusername    schedule 08.03.2010    source источник
comment
То, что вы перечислили, это не ошибка компилятора, это примечание. Посмотрите на реальную ошибку.   -  person Kaz Dragon    schedule 08.03.2010


Ответы (4)


Нет, не совсем. ifstream не имеет конструктора копирования, и если вы попытаетесь вернуть его, это означает копирование экземпляра в вашей функции туда, куда должен идти возврат.

Обычный обходной путь - передать ссылку на один и изменить эту ссылку в вашей функции.

Изменить: хотя это позволит вашему коду работать, это не решит основную проблему. Прямо сейчас вы смешиваете две довольно разные обязанности в одной функции: 1) получить имя файла, 2) открыть или создать этот файл. Я думаю, что если вы разделите их, код станет проще и значительно упростит устранение источника проблемы, которую вы видите.

Редактировать 2: Использование такой ссылки отлично работает без operator=. Общая идея примерно такая:

int open_file(char const *name, fstream &stream) { 
    stream.open(name);
}

Оператор присваивания в этом случае не нужен и не полезен — мы просто используем существующий поток fstream через ссылку. operator= был бы необходим в том и только в том случае, если бы нам нужно было передать аргумент ctor. С потоком мы можем по умолчанию создать поток, который не подключается к файлу, а затем использовать open для подключения к файлу постфактум.

person Jerry Coffin    schedule 08.03.2010
comment
Спасибо! Я сделал это, но, к сожалению, код все еще не работает. Использование базы данных ifstream = *getFile(); Я получаю сообщение об ошибке в этой строке main.cpp:29: примечание: синтезированный метод 'std::basic_ifstream‹char, std::char_traits‹char› ›::basic_ifstream(const std::basic_ifstream‹char, std::char_traits ‹char› ›&)' здесь требуется сначала - person wrongusername; 08.03.2010
comment
Трюк с передачей ссылки, чтобы избежать возврата нового объекта, будет работать только для типов, у которых есть operator=. В std::ifstream этого тоже нет, так что этот совет вам не подойдет. - person MSalters; 08.03.2010

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

person Remus Rusanu    schedule 08.03.2010
comment
Хорошо... Я вставил взамен & Third, и теперь он предупреждает меня, что возвращен адрес локальной переменной «therth». что мне делать в main() тогда? - person wrongusername; 08.03.2010
comment
никогда не возвращайте &stack_variable. Выделите ifstream с новым оператором и верните указатель. - person Remus Rusanu; 08.03.2010
comment
Вы имеете в виду что-то вроде ifstream returnStream = return returnStream;? кстати, это тоже не работает. выдает ошибку: main.cpp:56: error: преобразование из 'std::ifstream*' в нескалярный тип 'std::ifstream', запрошенный main.cpp:57: error: недопустимое преобразование из 'void*' в 'std::ifstream*' - person wrongusername; 08.03.2010
comment
Неправильно, я очень серьезно и настоятельно рекомендую вам прочитать об управлении памятью C/C++, прежде чем продолжить. Просто слишком много вещей, которых ты не знаешь. - person Remus Rusanu; 08.03.2010
comment
Спасибо! конечно ... я давным-давно, но, должно быть, забыл большую часть этого :) - person wrongusername; 08.03.2010

Как вариант, ifstream может быть расширен и к новому классу добавлен пользовательский конструктор.

Я расширил его, создав поток тестовых ресурсов, инкапсулировав в него поиск тестовых ресурсов.

// test_utils.h
class TestResourceStream : public std::ifstream {
    public:
        TestResourceStream(const char* file_path);
};
// test_utils.cpp
namespace fs = std::filesystem;
fs::path test_resource_path(const char* file_path) {
    fs::path path{std::string{"tests/resources/"} + file_path};
    if (!fs::exists(path))
        throw std::runtime_error{std::string{"path "} + 
            fs::absolute(path).c_str() + " does not exist"};
    return path;
}
TestResourceStream::TestResourceStream(const char* file_path)
    :std::ifstream{test_resource_path(file_path).c_str()} {}
// usage in test
TEST_CASE("parse") {
    std::list<GosDump::Expertise> expertises;
    TestResourceStream stream("requests/page_response.json");
    GosDump::Json::parse(expertises, stream);
    REQUIRE(10 == expertises.size());
}
person Nikita Karatun    schedule 05.02.2019

person    schedule
comment
Спасибо!!! немного поработав с вашим кодом, я наконец получил то, что хотел. :D - person wrongusername; 08.03.2010
comment
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода. - person NathanOliver; 18.10.2016