Search code examples
c++istream

How can you pass an std::istream into a function in a way that allows to pass temporaries?


I am trying to create a constructor to load a resource from any istream given to it. I cannot seem to figure out the best way to pass the istream parameter into a constructor.

Loader::Loader(istream stream);

This one is obviosly bad due to object slicing, so no option.

Loader::Loader(istream& stream);

This is what I am using now and seems fairly alright. It has one significant issue though - you can't give it a temporary since temporaries cannot bind to non-const references! For example, the following won't work:

Container():
  mLoader(ifstream("path/file.txt", ios::binary)
{
}

This is rather a limitation since I am now forced to store the ifstream as a member variable of Container just to extend its lifetime.

Since the problem is with non-const references, one could have though of this:

Loader::Loader(const istream& stream);

But since .seek() etc are non-const, this is not an option either...

So, how can this problem be solved in a neat way?


Solution

  • if your compiler is c++11 or better you can simply provide a version of the constructor that takes the istream as an r-value reference:

    void Loader::Loader(std::istream&& is)

    quick example:

    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <sstream>
    
    struct Loader
    {
        Loader(std::istream& is)
        {
            read(is);
        }
    
        Loader(std::istream&& is)
        {
            read(is);
        }
    
        void read(std::istream& is)
        {
            is >> std::quoted(x);
            is >> std::quoted(y);
        }
    
        std::string x, y;
    };
    
    std::ostream& operator<<(std::ostream& os, const Loader& l)
    {
        os << "x = " << l.x;
        os << ", y = " << l.y;
        return os;
    }
    
    
    auto main() -> int
    {
        using namespace std;
    
        Loader l(istringstream(R"text("donkey" "horse")text"));
        cout << l << endl;
    
        istringstream not_temp(R"text("apple" "banana")text");
        Loader l2(not_temp);
        cout << l2 << endl;
    
        return 0;
    }
    

    expected output:

    x = donkey, y = horse
    x = apple, y = banana