The following code compiles and runs fine, but does not work as intended (compare the comments in the code). I believe the reason is that the lifetime of is
ends with getFileIter
, so the stream buffer iterator has nothing left to iterate over. (By contrast, I think I remember that in C#, is
would live as long as the stream buffer iterator points to, but that does not seem to be the case in C .)
One fix to avoid this is using ... *is = new std::ifstream(p)
, but this means I have to take care of the pointer myself when I'm done.
Is there any mechanism to link the lifetimes of is
and its stream buffer iterator?
// g test.cpp -o test && ./test
#include <iostream>
#include <fstream>
std::istreambuf_iterator<char> getFileIter(std::string p)
{
std::ifstream is(p);
//std::ifstream *is = new std::ifstream(p);
std::cout << "Read " << p << "? " << is.good() << std::endl;
return std::istreambuf_iterator<char>(is.rdbuf());
}
int main()
{
std::string p1("/etc/passwd");
std::string p2("/etc/group");
// Prints 1 as if the two files were identical
std::cout << std::equal(
getFileIter(p1),
std::istreambuf_iterator<char>(),
getFileIter(p2)
) << std::endl;
// Prints nothing except /////
auto x = getFileIter(p1);
int i = 0;
do
{
std::cout << *(x ) << "/";
i ;
} while (i < 5);
std::cout << std::endl;
return 0;
}
CodePudding user response:
As one comment suggests, combine both things: stream and its iterator in a struct - to ensure the iterator will not outlife the stream:
struct FileIter
{
FileIter(const std::string& p)
: is(p), iter(is.rdbuf())
{}
std::ifstream is;
std::istreambuf_iterator<char> iter;
};
std::cout << std::equal(
FileIter(p1).iter,
std::istreambuf_iterator<char>(),
FileIter(p2).iter
) << std::endl;
FileIter x(p1);
int i = 0;
do
{
std::cout << *(x.iter ) << "/";
i ;
} while (i < 5);
std::cout << std::endl;
If you like -- you might make this FileIter looks like iterator - see this post
Two important things:
In this code:
std::cout << std::equal(
FileIter(p1).iter,
std::istreambuf_iterator<char>(),
FileIter(p2).iter
) << std::endl;
FileIter
might be temporary (nameless) because its lifetime is guaranteed to be as long as to the end of expression (the semicolon).
Here, you cannot replace FileIter x(p1);
with auto x = FileIter(p1).iter;
because stream in FileIter
will end its lifetime with the end of expression (semicolon here).
FileIter x(p1);
int i = 0;
do
{
std::cout << *(x.iter ) << "/";
i ;
} while (i < 5);
std::cout << std::endl;