mallea
mallea

Reputation: 564

Can't we manage std::map<string,ofstream>?

I tried to create for outputting the timing results and call any ofstream from pre-defined string:

#include <cstring>                                                                                                                  
#include <map>
#include <fstream>

using namespace std;

int main(void) {
    map<string,ofstream> Map;
    ofstream ofs("test_output");
    string st="test";
    Map[st] = ofs;
    return 0;
}

I got the following error; how can I fix it?

a.cpp: In function ‘int main()’:
a.cpp:11:8: error: use of deleted function ‘std::basic_ofstream<_CharT, _Traits>& std::basic_ofstream<_CharT, _Traits>::operator=(const std::basic_ofstream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’
  Map[s]=ofs;
        ^
In file included from a.cpp:3:0:
/usr/include/c++/5/fstream:744:7: note: declared here
   operator=(const basic_ofstream&) = delete;

       ^
In file included from a.cpp:3:0:
/usr/include/c++/5/fstream:744:7: note: declared here
   operator=(const basic_ofstream&) = delete; 

Upvotes: 4

Views: 846

Answers (2)

zett42
zett42

Reputation: 27776

As an std::ostream is not copyable (copy constructor and assignment operator are marked deleted), you have to either construct the ofstream directly in the map (e.g. using std::map::emplace()) or use move assignment.

Construct in-place

There are basically two ways, either default-construct stream in the map (pre C++11), or call std::map::emplace() to supply ofstream constructor arguments.

Using default-construction (works even pre C++11):

map<string,ofstream> m;

// default-construct stream in map    
ofstream& strm = m["test"];
strm.open("test_output");
strm << "foo";

Using emplacement:

// 1st parameter is map key, 2nd parameter is ofstream constructor parameter    
auto res = m.emplace("test", "test_output");
auto& strm = res.first->second;
strm << "bar";

Move assignment

We can construct the stream outside of the map first, turn it into an rvalue by calling std::move() and use move assignment operator to move it into the map:

map<string,ofstream> m;    
ofstream strm("test_output");
m["test"] = std::move( strm );
// strm is not "valid" anymore, it has been moved into the map

We can even get rid of std::move() if we directly create the stream as an rvalue:

m["test"] = ofstream("test_output");

Move assignment is less efficient than the other methods, because first a stream will be default-constructed in the map, just to be replaced by the move-assigned stream then.

Live demo of all three methods.

Note: Sample code omitts any error handling for brevity. State of stream should be checked after opening and after each stream operation.

Upvotes: 5

Rerito
Rerito

Reputation: 6086

This comes from the fact that you can't copy an ostream you can only move it. You get this error because the copy assignment operator is deleted. Instead the map must take ownership of the stream:

Map[st] = std::move(ofs);

Now this also means that you will have to be careful when iterating over your map. You must avoid copies and also avoid stealing the ownership from the map.

On a side note, please note that using namespace std; is not recommended.

Upvotes: 4

Related Questions