14

これはおそらくあなたにとって非常に厄介な質問です:関数から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 

編集:Remusが提案したように、代わりにポインターを返すように関数を変更し、main()の行を「ifstreamdatabase = * 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
4

4 に答える 4

18

いいえ、そうではありません。ifstreamにはコピーコンストラクターがありません。コピーコンストラクターを返そうとすると、関数内のインスタンスを、返される必要がある場所にコピーすることを意味します。

通常の回避策は、1つへの参照を渡し、関数でその参照を変更することです。

編集:それはあなたのコードが機能することを可能にしますが、それは基本的な問題を修正しません。現在、2つのかなり異なる責任を1つの機能に混合しています。1)ファイル名を取得する、2)そのファイルを開く、または作成する。これらを分離すると、コードが単純になり、発生している問題の原因を簡単に排除できるようになると思います。

編集2:このような参照を使用すると、がなくても完全に機能しoperator=ます。一般的な考え方は次のようなものです。

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

この場合、代入演算子は必要でも有用でもありません。参照を介して既存のfstreamを使用するだけです。引数をctorに渡さなければならない場合にのみoperator=必要です。ストリームを使用すると、デフォルトでファイルに接続しないストリームを作成し、openを使用して事後にファイルに接続できます。

于 2010-03-08T05:08:53.433 に答える
8
bool checkFileExistence(const string& filename)
{
    ifstream f(filename.c_str());
    return f.is_open();
}

string getFileName()
{
    string filename;
    cout << "Please enter in the name of the file you'd like to open: ";
    cin >> filename;
    return filename;
}

void getFile(string filename, /*out*/ ifstream& file)
{
    const bool file_exists = checkFileExistence(filename);
    if (!file_exists) {
        cout << "File " << filename << " not found." << endl;
        filename = getFileName();  // poor style to reset input parameter though
        ofstream dummy(filename.c_str();
        if (!dummy.is_open()) {
            cerr << "Could not create file." << endl;
            return;
        }
        cout << "File created." << endl;
    }
    file.open(filename.c_str());
}

int main()
{
    // ...
    ifstream file;
    getFile("filename.ext", file);
    if (file.is_open()) {
        // do any stuff with file
    }
    // ...
}
于 2010-03-08T07:25:47.363 に答える
4

ifstreamはコピーコンストラクトセマンティクス(エラーメッセージが基本的に言っていること)をサポートしていないため、ifstreamを返すことはできません。代わりにifstream*を返し、割り当てポインターを削除する責任を呼び出し元に渡します。

于 2010-03-08T05:07:22.780 に答える
0

オプションとして、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());
}
于 2019-02-05T09:15:06.070 に答える