10

我正在尝试编写自己的日志记录类并将其用作流:

logger L;
L << "whatever" << std::endl;

这是我开始的代码:

#include <iostream>

using namespace std;


class logger{
public:
    template <typename T>
    friend logger& operator <<(logger& log, const T& value);
};

template <typename T>
logger& operator <<(logger& log, T const & value) {
    // Here I'd output the values to a file and stdout, etc.
    cout << value;
    return log;
}

int main(int argc, char *argv[])
{
    logger L;
    L << "hello" << '\n' ; // This works
    L << "bye" << "alo" << endl; // This doesn't work
    return 0;
}

但是我在尝试编译时遇到错误,说没有 operator<< 的定义(使用 std::endl 时):

pruebaLog.cpp:31: error: no match for ‘operator<<’ in ‘operator<< [with T = char [4]](((logger&)((logger*)operator<< [with T = char [4]](((logger&)(& L)), ((const char (&)[4])"bye")))), ((const char (&)[4])"alo")) << std::endl’

所以,我一直试图重载 operator<< 来接受这种流,但这让我发疯。我不知道该怎么做。例如,我一直在查看 ostream 头文件中 std::endl 的定义,并使用此头文件编写了一个函数:

logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))

但没有运气。我尝试过使用模板而不是直接使用 char,也尝试过简单地使用“const ostream& os”,但什么也没有。

让我烦恼的另一件事是,在错误输出中, operator<< 的第一个参数发生了变化,有时它是对指针的引用,有时看起来像双重引用......

4

4 回答 4

9

endl是一头奇异的野兽。它不是一个常数值。它实际上是一个函数。您需要一个特殊的覆盖来处理以下应用endl

logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
  cout << pf;
  return log;
}

这接受插入一个接受ostream引用并返回ostream引用的函数。就是这样endl

编辑:回应 FranticPedantic 的“为什么编译器不能自动推断出这个?”这个有趣的问题。原因是如果你再深入研究,endl实际上它本身就是一个模板函数。它定义为:

template <class charT, class traits>
  basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );

也就是说,它可以将任何类型ostream作为其输入和输出。所以问题不在于编译器无法推断出它T const &可能是一个函数指针,而是它无法确定您要传入哪个 。问题中提供的模板版本将接受指向任何函数的指针作为它的第二个参数,但同时,模板代表了一组无限的潜在函数,所以编译器不能在那里做任何有意义的事情。endloperator<<endl

提供operator<<其第二个参数与模板的特定实例匹配的特殊重载endl允许调用解析。

于 2010-05-10T14:42:14.143 回答
5

endl是一个 IO 操纵器,它是一个函子,它通过引用接受流,对其执行一些操作,并通过引用返回该流。cout << endl等价于cout << '\n' << flush,其中flush是刷新输出缓冲区的操纵器。

在您的课程中,您只需要为此运算符编写一个重载:

logger& operator<<(logger&(*function)(logger&)) {
    return function(*this);
}

通过引用logger&(*)(logger&)接受和返回 a 的函数的类型在哪里。logger要编写自己的操纵器,只需编写一个与该签名匹配的函数,并让它对流执行一些操作:

logger& newline(logger& L) {
    return L << '\n';
}
于 2010-05-10T14:40:13.587 回答
0

在 C++ 中,它是封装底层 I/O 机制的流缓冲区。流本身只封装了对字符串的转换以及 I/O 方向。

因此,您应该使用预定义的流类之一,而不是自己制作。如果你有一个新的目标,你希望你的 I/O 去(比如系统日志),你应该创建的是你自己的流缓冲区(派生自std::streambuf)。

于 2010-05-10T14:39:49.707 回答
0

我认为问题在于您的流不会重载以接受与此答案中所示operator<<类型相同的函数: std::endl is of unknown type when overloading operator<<std::endl

于 2010-05-10T14:44:08.013 回答