If we are expecting just one type of data from an input stream, we can easily put them in a container using std::istream_iterator.

We have a file filled with integers, as an example we can think to a file created in this way:

const char* const filename = "someStuff.dat";

std::ofstream ofs(filename);
for(int i = 0; i < 10; ++i)
ofs << i << ' ';

To read it, we can think to loop on a ifstream open on that file, getting one single integer at time and pushing it in our container:

std::ifstream ifs(filename);

std::list<int> data;
int value;
ifs >> value;



We know that is better to use range member methods, so we rewrite our solution using istream_iterator:

std::ifstream ifs(filename);

std::istream_iterator<int> dataBegin(ifs);
std::istream_iterator<int> dataEnd;
std::list<int> data(dataBegin, dataEnd);


Notice that we create the "begin" istream_iterator specifying the type we are expecting in the file, and an ifstream to the file itself.
The "end" istream_iterator requires just the data type.

Do we really need to give names to the iterators? Actually not. They are used only in the list ctor, so we could create them there on the fly. The issue in doing this, is that we have to pay attention in writing it right:

std::ifstream ifs(filename);

std::list<int> data((std::istream_iterator<int>(ifs)), std::istream_iterator<int>());


The parentheses around the first iterator in the list ctor are mandatory. If you don't put them, for the compiler we are declaring a function, and not defining a list object.

I love the Effective C++ books by Scott Meyers. Currently, I'm reading the STL instalment. This post is written in connection to its Item 6.

No comments:

Post a Comment