首页 > 解决方案 > 如何在 std::ofstream 和 std::cerr 之间切换

问题描述

我想编写一个函数,该函数返回一个写入文件或 stderr 的 ostream。

我的第一次尝试:

#include <fstream>
#include <iostream>

std::ostream get_stream() {
    bool flag = (time(nullptr) % 2); // stand-in
    if (flag)
        return std::cerr;
    else
        return std::ofstream{"somefile.txt"};
}

int main() {
    auto logger {get_stream()};
    logger << "Just testing, everything is fine."
           << std::endl;
}

这会因(长)编译器错误而失败 - 我怀疑是因为std::cerr没有复制构造函数。另一个变体(返回引用)不起作用,因为ofstream是局部变量。我可以在堆上分配ofstream,但是调用者不知道是否需要释放指针(当我们返回对 的引用时它不会std:cerr)。

所以,我写了第2版:

#include <fstream>
#include <iostream>

struct Logger {
    std::ofstream ostream;
    bool to_stderr {true};

    template<typename A>
    Logger& operator<<(A rhs) {
        if (to_stderr)
            std::cerr << rhs;
        else
            ostream << rhs;
        return this;
    }
};

int main() {
    Logger logger;
    logger << "Just testing, everything is fine."
           << std::endl;
}

这也会失败,并出现一个长编译错误,该错误开头为:

$ g++ -Wall -o v2 v2.cpp
v2.cpp: In function ‘int main()’:
v2.cpp:30:12: error: no match for ‘operator<<’ (operand types are ‘Logger’ and ‘&lt;unresolved overloaded function type>’)
     logger << "Just testing, everything is fine."
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            << std::endl;
            ^~~~~~
v2.cpp:9:13: note: candidate: template<class A> Logger& Logger::operator<<(A)
     Logger& operator<<(A rhs) {
             ^~~~~~~~
v2.cpp:9:13: note:   template argument deduction/substitution failed:
v2.cpp:30:20: note:   couldn't deduce template parameter ‘A’
            << std::endl;
                    ^~~~
[...]

为什么第二个版本不起作用,实现这样的事情的正确方法是什么?

使用 << 运算符同时写入文件和 cout是一个相关问题,但据我所知,解决方案的唯一区别是operator<<在类外部定义的,如果我对我的代码,我仍然得到同样的错误)

标签: c++stl

解决方案


您只需要将文件流提升到全局状态。这可以是全局变量、静态成员或(我最喜欢的)块局部静态变量。这种方法确保文件流在第一次调用之前不会打开,并且在程序终止之前保持打开状态。

从那里您只需通过引用返回流。

std::ostream &get_stream() {
    static std::ofstream file("somefile.txt");
    return flag ? std::cerr : file;
}

推荐阅读