为什么将字符串输出到未命名的 std::ofstream 反而给了我一个十六进制数?

Why does output of a string to unnamed std::ofstream give me a hex number instead?

本文关键字:十六进制数 一个 输出 字符串 未命名 为什么 ofstream std      更新时间:2023-10-16

我试图将一些调试输出添加到 C++03 项目中,并得到了一些奇怪的结果。以下是简化的测试代码:

#include <fstream>
int main()
{
{
std::ofstream file("/tmp/test.txt");
file << "hello" << " ... OK, this worksn";
}
std::ofstream("/tmp/test.txt",std::ios_base::app) << "hello"
<< " ... no, I mean hello!n";
}

出于某种原因,这是我在编译后得到的:

$ g++ test.cpp -o test && ./test && cat /tmp/test.txt
hello ... OK, this works
0x80487fe ... no, I mean hello!

为什么在将字符串输出到未命名的对象时会得到一个十六进制数std::ofstream?为什么第二个字符串的后续输出会起作用?

我们用于将 C 字符串传递给std::ostream的常用operator<<被声明为自由函数

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
const char* s );

未命名的std::ofstream对象是一个临时变量,临时变量不能绑定到非常量引用,因此此运算符重载不参与重载解析。取而代之的是采用最接近的匹配项,即成员函数

std::basic_ostream& std::basic_ostream::operator<<(const void*);

,它采用文字擦除的指针并仅打印其值。由于可以在对象是临时对象的情况下调用成员函数,因此这个函数确实可行。这解释了输出中的十六进制数。现在,此运算符返回一个引用,std::basic_ostream&。由于这不再是一个临时对象,而是对一些非常量对象的引用,因此可以成功调用通常需要const char*operator<<的自由函数重载。这就是按预期打印第二个字符串的原因。

请注意,从 C++11 开始,代码将按预期工作,因为那里我们有一个额外的重载operator<<,它采用右值引用:

template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os, 
const T& value );

,并且临时确实绑定到右值引用。

若要使代码在 C++03 中工作,可以使用成员函数std::ostream::flush(),该函数返回对对象的非常量引用,并且对fstream对象没有任何用户可见的副作用:

#include <fstream>
int main()
{
std::ofstream("/tmp/test.txt").flush() << "hello"
<< " ... OK, this now works toon";
}