我在清理旧C / C ++应用程序的调试宏时遇到了这个问题:我们有一个继承自ostrstream
的Tracer类(我知道它自C ++ 98以来已被弃用,但这个应用程序是写于1998年!)我们这样使用:
Tracer() << "some" << " message" << " here";
现在,如果链中的第一个值是上面的常量字符串,则在Tracer上调用ostrstream::str()
的结果(在析构函数中完成,将结果插入队列中)包含十六进制表示形式指向此字符串而不是文本的指针。因此,上述陈述会产生类似"0x401a37 message here"
的内容。旧宏不会发生这种情况,因为它们总是有一个长(线程ID)作为第一个已被删除的值。
使用gdb进入它会显示,对于第一次插入,这会在ostrstream上调用operator<<(void const*)
,而后续插入会调用operator<< <...>(basic_ostream<...>&, char const*)
(为了便于阅读,会删除模板)。
有人可以解释这种行为吗?什么是一个干净的方法来解决这个问题?我找到了一个简单的解决方法,它使用<< left
作为第一个参数 - 这样安全吗?有更好的方法吗?
这是一个最小化的例子:
#include <strstream>
#include <iostream>
using namespace std;
class Trace : public ostrstream {
public:
Trace();
virtual ~Trace();
};
Trace::Trace() : ostrstream() {}
Trace::~Trace() {
static_cast< ostrstream& >(*this) <<ends;
char * text = ostrstream::str();
cout << "MESSAGE: "<< text <<endl;
delete[] text;
}
int main(){
Trace() << "some" << " text" << " here";
Trace() << left << "some" << " text" << " here";
Trace() << 123 << " text" << " here";
}
答案 0 :(得分:5)
首先请注意,以operator<<
为参数的const char*
是非成员函数。并且存在一个以void const*
为参数的成员函数。
在您的代码中,表达式Trace() << "xyz"
只能调用成员函数,因为Trace()
创建了一个temporay,它不能绑定到非成员operator<<
函数的第一个参数,因为这些函数将第一个参数作为std::ostream&
,这是非const引用。所以Trace() << "xyz"
解析为成员 operator<<
,它以void*
为参数,打印地址!
我的建议:
std::ostrstream
已弃用)。operator<<
以下是一个例子:
#include <sstream> //for std::ostringstream
struct Trace
{
std::ostringstream ss;
template<typename T>
Trace& operator << (T const & data)
{
ss << data;
return *this;
}
~Trace()
{
std::cout << ss.str() << std::endl;
}
};
现在您可以将其用作:
Trace() << "Hello World\n" << 100 << "\nBye\n";
输出:
Hello World
100
Bye
答案 1 :(得分:5)
它以这种方式工作,因为Tracer()
是一个临时(rvalue),不能绑定到operator<<(basic_ostream<...>&,
中的非const引用。
但是,您可以调用operator<<(void const*)
之类的成员函数,因为这不需要左值。
成员函数然后返回对流对象的引用,可以用于调用序列中的下一个operator<<
。
以这种方式调用任何成员函数,例如Tracer() << left
或Tracer() << flush
,从而将引用“转换”为左值引用是非常安全的。
如果您碰巧拥有符合C ++ 11标准的编译器,标准库甚至包含一个operator<<(basic_ostream<...>&&,
,它会为您执行此操作。在这种情况下,您不再需要解决方法。