我正在尝试编写自己的日志记录类并将其用作流:
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’
所以,我一直在尝试重载运算符<<接受这种流,但这让我发疯。我不知道该怎么做。例如,我一直在查看 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<< 的第一个参数是变化,有时是对指针的引用,有时看起来像双重引用......
I'm trying to write my own logging class and use it as a stream:
logger L;
L << "whatever" << std::endl;
This is the code I started with:
#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;
}
But I was getting an error when trying to compile, saying that there was no definition for operator<< (when using 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’
So, I've been trying to overload operator<< to accept this kind of streams, but it's driving me mad. I don't know how to do it. I've been loking at, for instance, the definition of std::endl at the ostream header file and written a function with this header:
logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))
But no luck. I've tried the same using templates instead of directly using char, and also tried simply using "const ostream& os", and nothing.
Another thing that bugs me is that, in the error output, the first argument for operator<< changes, sometimes it's a reference to a pointer, sometimes looks like a double reference...
发布评论
评论(4)
endl
是一头奇怪的野兽。它不是一个恒定值。实际上,它是一个函数。您需要一个特殊的重写来处理endl
的应用:它接受插入一个接受
ostream
引用并返回ostream
引用的函数。这就是endl
。编辑:回应 FranticPedantic 的有趣问题“为什么编译器不能自动推断出这个?”。原因是,如果你更深入地研究,
endl
实际上本身就是一个模板函数。它的定义是:也就是说,它可以采用任何类型的
ostream
作为其输入和输出。因此,问题不在于编译器无法推断出T const &
可能是函数指针,而是它无法找出哪个endl
你打算传入。问题中提出的operator<<
的模板化版本将接受指向任何函数的指针作为其第二个参数,但同时,< code>endl 模板代表一组无限的潜在函数,因此编译器不能在那里做任何有意义的事情。提供运算符<<的特殊重载(其第二个参数与
endl
模板的特定实例相匹配)允许调用解析。endl
is a strange beast. It isn't a constant value. It's actually, of all things, a function. You need a special override to handle the application ofendl
:This accepts insertion of a function that takes an
ostream
reference and returns anostream
reference. That's whatendl
is.Edit: In response to FranticPedantic's interesting question of "why can't the compiler deduce this automatically?". The reason is that if you delve yet deeper,
endl
is actually itself a template function. It's defined as:That is, it can take any sort of
ostream
as its input and output. So the problem isn't that the compiler can't deduce thatT const &
could be a function pointer, but that it can't figure out whichendl
you meant to pass in. The templated version ofoperator<<
presented in the question would accept a pointer to any function as its second argument, but at the same time, theendl
template represents an infinite set of potential functions, so the compiler can't do anything meaningful there.Providing the special overload of the
operator<<
whose second argument matches a specific instantiation of theendl
template allows the call to resolve.endl
是一个 IO 操纵器,它是一个函子,它通过引用接受流,对其执行某些操作,并同样通过引用返回该流。cout << endl
等价于cout << '\n' <<刷新
,其中flush
是刷新输出缓冲区的操纵器。在您的类中,您只需为此运算符编写一个重载:
其中
logger&(*)(logger&)
是接受和返回logger
的函数的类型通过参考。要编写自己的操纵器,只需编写一个与该签名匹配的函数,并让它在流上执行一些操作:endl
is an IO manipulator, which is a functor that accepts a stream by reference, performs some operation on it, and returns that stream, also by reference.cout << endl
is equivalent tocout << '\n' << flush
, whereflush
is a manipulator that flushes the output buffer.In your class, you just need to write an overload for this operator:
Where
logger&(*)(logger&)
is the type of a function accepting and returning alogger
by reference. To write your own manipulators, just write a function that matches that signature, and have it perform some operation on the stream:我认为问题是您的流不会重载
operator<<
来接受与std::endl
类型相同的函数,如本答案所示: 重载运算符时 std::endl 的类型未知<<I believe that the problem is your stream doesn't overload
operator<<
to accept a function that has the same type asstd::endl
as illustrated in this answer: std::endl is of unknown type when overloading operator<<在 C++ 中,流缓冲区封装了底层 I/O 机制。流本身只封装了字符串的转换和I/O方向。
因此,您应该使用预定义的流类之一,而不是创建自己的流类。如果您希望 I/O 转到一个新目标(例如系统日志),那么您应该创建自己的流缓冲区(源自
std::streambuf< /代码>)。
In C++ it is the stream buffer that encapsulates the underlying I/O mechanisim. The stream itself only encapsulates the conversions to string, and the I/O direction.
Thus you should be using one of the predefined stream classes, rather than making your own. If you have a new target you want your I/O to go to (like a system log), what you should be creating is your own stream buffer (derived from
std::streambuf
).