2010-05-10 155 views
10

我试图写我自己的日志记录类,并把它作为一个流时:C++作为一个参数流重载运算符<<

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; 
} 

但我正在试图编译时错误,说有是为运营商< <没有定义(使用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”,而不是。

另一件事,我的错误是,在错误输出,为运营商< <变化的第一个参数,有时这是一个指针引用,有时看起来像一个双参考...

+0

[性病:: ENDL的可能重复的是未知类型当重载运算符<<](http://stackoverflow.com/questions/1134388/stdendl-is-of-unknown-type-when-overloading-operator) – sth 2010-05-10 14:41:45

+1

好问题。上周我遇到了同样的问题。在下面的答案中的经验悲伤的声音... – 2010-05-10 14:42:12

+0

@ T.E.D .:嗯...谢谢你? – 2010-05-10 14:46:21

回答

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 &可能是一个函数指针,但它不能计算出哪个endl你打算通过。问题中提出的模板版本operator<<将接受一个指向任何函数的指针作为它的第二个参数,但同时,endl模板代表了一组潜在的函数,所以编译器不能做任何有意义的工作。

提供了其第二个参数中特定实例化endl模板匹配operator<<的特殊超载允许呼叫解决。

+0

谢谢!这解决了它。我发现我的定义'basic_ostream >&(* s)(basic_ostream >&)'是ostream的一个通用版本,不起作用因为它被定义为“const”。一旦我放弃了常量,它就起作用了。 – 2010-05-10 14:48:49

+0

对我来说,真正的问题仍然存在,这就是为什么编译器不能将T推导为函数指针并将其绑定到它? – 2010-05-10 14:56:50

+0

@FranticPedantic有趣的一点!在答案中编辑。 – 2010-05-10 15:19:11

0

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

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

5

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

在你的类,你只需要编写一个重载运算符:

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

哪里logger&(*)(logger&)是接受并通过引用返回logger函数的类型。写自己的操纵,只写该签名匹配的功能,并使其在流执行一些操作:

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

这比接受的答案更复杂,但仍然是一个有趣的方法。 – 2010-05-10 14:53:47

+0

@FranticPedantic:真的是同一枚硬币的两面。 – 2010-05-10 15:03:59