0

There is a similar question to this one but it was not answered.

I am trying to create a map of std::ofstream objects but I cannot build the code.

So far I have tried

//std::map<std::string, std::ofstream> my_files;  //this fails
std::map<std::string, std::ofstream& > my_files;

for(auto & member: members){
   filename=GetFileName();
   //create the file
   std::ofstream thefile(filename);

   my_files[member.first]=thefile;
}
//Here I use the ofstreams in the map to write etc

I tried the first (commented) line and I got this error. Use of deleted function. So I change it to the line above, but I still get the same error error: use of deleted function ‘std::basic_ofstream<

How can I build a map of ofstream objects?

Note: In the similar question, someone suggested to use a map of strings but the reason to use a map of ofstream is that I am not going to be opening and closing the file everytime I want to do a minimal change to each.

KansaiRobot
  • 7,564
  • 11
  • 71
  • 150
  • 1
    Streams can't be copied. You may be able to move them or use emplace to construct them in-place but I've never tried myself. – Retired Ninja Dec 23 '21 at 06:24
  • Then what is a good way to handle several streams at the same time? – KansaiRobot Dec 23 '21 at 06:26
  • 2
    My personal feeling is that it's better to not keep files open unless you're actively reading or writing them. I'd read whatever data you need, manipulate it in memory, and then write it all out at once. – Retired Ninja Dec 23 '21 at 06:31
  • 1
    @RetiredNinja Depending on the size of the files, that might not be feasible. – Etienne de Martel Dec 23 '21 at 06:34
  • "Then what is a good way to handle several streams at the same time? " Depends what you mean by "handle". What actual problem are you trying to solve? Why do you find it useful to have multiple files open simultaneously? You are aware that file handles are typically a [limited system resource](https://stackoverflow.com/questions/1356675/check-the-open-fd-limit-for-a-given-process-in-linux), yes? – Karl Knechtel Dec 23 '21 at 06:43

2 Answers2

1

The problem is that a reference is not object by itself. A reference refers to some other object. So you cannot store a reference in a container.

You can solve this by replacing my_files[member.first]=thefile; with:

my_files.emplace(member.first, std::ofstream(filename));

Also, replace std::map<std::string, std::ofstream& > my_files; with:

std::map<std::string, std::ofstream > my_files; //removed the &

Modified Code

std::map<std::string, std::ofstream > my_files; //removed the &

for(auto & member: members){
   filename=GetFileName();

   my_files.emplace(member.first, std::ofstream(filename)); //ADDED THIS
}
Jason
  • 36,170
  • 5
  • 26
  • 60
  • would it be the same if I do `my_files[member.first]=std::ofstream(filename);`? – KansaiRobot Dec 23 '21 at 06:41
  • @KansaiRobot Yes you can write `my_files[member.first]=std::ofstream(filename);`. Also just for further reading, from [this](https://stdcxx.apache.org/doc/stdlibug/34-2.html) article, *Stream objects cannot simply be **copied** and **assigned**.*. And note that `std::ofstream` has a **deleted copy constructor** so it can't be copied. But you can `move` them using `std::move`. – Jason Dec 23 '21 at 07:16
  • @KansaiRobot Yes you can also use `my_files[member.first]=std::ofstream(filename);`. But note that `my_files[member.first]=std::ofstream(filename);` will use [**`std::ofstream::operator=`**](https://en.cppreference.com/w/cpp/io/basic_ofstream/operator%3D) – Jason Dec 23 '21 at 09:07
1

First of all, references cannot be stored in containers. But you seem to want to store the std::ofstream itself, not a reference to it, anyway, since your std::ofstream object is otherwise going to be destroyed at the end of the loop body leaving you with a dangling reference in the map, so:

std::map<std::string, std::ofstream > my_files;

Secondly, stream objects such as std::ofstream are not copyable, so you cannot simply copy them into the map.

However, they are movable, so you can move them into the map:

my_files[member.first] = std::move(thefile);

(requires #include<utility>) or alternatively, by constructing it directly in the assignment:

my_files[member.first] = std::ofstream(filename);

For explanation of std::move if you haven't seen it before, see this question.

user17732522
  • 53,019
  • 2
  • 56
  • 105