Search code examples
c++design-patternscopy-constructorofstream

Insert instance of ofstream to container


I created my a class file which is a wrapper for std::ofstream. I created a Container to contain all the instances of file.

class file
{
private:
      std::ofstream ofs;

public:
      void open(std::string filename);
      void write(std::string s);
      void close();
};

class Container
{
private:
  std::map<int, file> m;


public:
      void insert(file f,int i)
      {
            m.insert(std::pair<int,file> (i,f));
      }
      void get(int i)
      {
            m.at(i);
      }
};

However, there is an issue in this code. In the insert method I am attempting to copy a std::pair<int,file> which cannot be done as the copy constructor of std::ofstream is deleted (see compilation error below).

I would like to successively add instances of file in a container. How can I do that?


Here is the compilation error

In file included from src/test.cpp:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iostream:38:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ios:216:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__locale:15:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:439:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:627:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/utility:274:23: error: call to implicitly-deleted copy constructor of
      'file'
        : first(__x), second(__y) {}
                      ^      ~~~
src/test.cpp:35:22: note: in instantiation of member function 'std::__1::pair<int, file>::pair' requested here
            m.insert(std::pair<int,file> (i,f));
                     ^
src/test.cpp:9:21: note: copy constructor of 'file' is implicitly deleted because field 'ofs' has a deleted copy constructor
      std::ofstream ofs;
                    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/fstream:1168:5: note: copy constructor is implicitly deleted because
      'basic_ofstream<char, std::__1::char_traits<char> >' has a user-declared move constructor
    basic_ofstream(basic_ofstream&& __rhs);
    ^
1 error generated.

Solution

  • If you want to kind of initialize your Container with file without copying them, then you could rely on the move-constructor of std::ofstream:

    void insert(file &&f, int i)
    {
        m.insert(std::pair<int,file>(i, std::move(f)));
    }
    

    Then you would do the following:

    cont.insert(file{}, 0); // Construct a file when inserting, you already have an rvalue
    

    Or:

    file myfile;
    myfile.open(...); // Do whatever you want with myfile...
    cont.insert(std::move(myfile), 0); // And then move it, as long as you are not using if after
    

    If your file is not move-constructible, you could construct it in place:

    template <typename... Args>
    void insert(int i, Args&&... args) {
        m.emplace(std::piecewise_construct, std::tuple<int>(i),
                  std::tuple<Args>(std::forward<Args>(args)...));
    }
    

    Also, if you want to insert default-initialized file (like in the first example), you could simply do:

    void insert(int i)
    {
        m[i];
    }