C++重载operator<<时将stream作为参数

发布于 2024-08-31 14:18:22 字数 1559 浏览 3 评论 0 原文

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

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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

阳光下的泡沫是彩色的 2024-09-07 14:18:22

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 & 可能是函数指针,而是它无法找出哪个 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 of endl:

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

This accepts insertion of a function that takes an ostream reference and returns an ostream reference. That's what endl 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:

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

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 that T const & could be a function pointer, but that it can't figure out which endl you meant to pass in. The templated version of operator<< presented in the question would accept a pointer to any function as its second argument, but at the same time, the endl 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 the endl template allows the call to resolve.

迷路的信 2024-09-07 14:18:22

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

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

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

其中 logger&(*)(logger&) 是接受和返回 logger 的函数的类型通过参考。要编写自己的操纵器,只需编写一个与该签名匹配的函数,并让它在流上执行一些操作:

logger& newline(logger& L) {
    return L << '\n';
}

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 to cout << '\n' << flush, where flush is a manipulator that flushes the output buffer.

In your class, you just need to write an overload for this operator:

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

Where logger&(*)(logger&) is the type of a function accepting and returning a logger by reference. To write your own manipulators, just write a function that matches that signature, and have it perform some operation on the stream:

logger& newline(logger& L) {
    return L << '\n';
}
风渺 2024-09-07 14:18:22

我认为问题是您的流不会重载 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 as std::endl as illustrated in this answer: std::endl is of unknown type when overloading operator<<

一个人的夜不怕黑 2024-09-07 14:18:22

在 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文