Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anonymous std::ofstream handles character array wrongly

Tags:

c++

stl

The following code outputs a string literal to a file with both anonymous and named streams:

#include <fstream>

using namespace std;

int main()
{
    ofstream("testfile") << "test" << endl;

    ofstream ofs ("testfile2");
    ofs << "test2" << endl;

    return 0;
}

As you can see from strace's output, only the named stream works:

open("testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "0x400a91\n", 9)               = 9
close(3)                                = 0
open("testfile2", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "test2\n", 6)                  = 6
close(3)                                = 0

Also, if you use std::string instead of a literal, it fails to compile.

Why is this?

like image 875
Rodrigo Queiro Avatar asked Sep 18 '12 16:09

Rodrigo Queiro


2 Answers

In C++03, the non-member operators that output character pointers and strings cannot be called with an rvalue, as they require an lvalue reference, so the member operator<<(const void *) is called. In C++11 this is solved by writing overloads that take an rvalue reference, but in C++03 you can work around it by calling a member function or member operator that returns an lvalue reference (note that non-const member functions can be called on rvalues):

ofstream("testfile").write("", 0) << "test" << endl;

You can easily write a manipulator to do this:

std::ios_base& (*do_nothing)(std::ios_base&) ) {}

ofstream("testfile") << do_nothing << "test" << endl;
like image 43
ecatmur Avatar answered Oct 12 '22 23:10

ecatmur


You have a problem that the "anonymous stream" is an rvalue, and with C++98 you can only call member functions on it. The stream << "test" will bind to a member taking a void* that outputs the pointer's address.

C++11 has added an operator<< that takes an rvalue stream, and that will make the code work.

like image 50
Bo Persson Avatar answered Oct 13 '22 01:10

Bo Persson