Skip to content

<filesystem>: Copy of directory_iterator doesn't reach end, when original is iterated till end #626

@YanivKap

Description

@YanivKap

Describe the bug
When I create a directory_iterator (or recursive_directory_iterator) and iterate it
with a range-loop (that actually creates a copy of the iterator, by calling std::filesystem::begin()),
the original iterator will be affected, but to an unexpected state, which is containing the data
of the last iterated entry (the one before the end).

That also means, that a second range-loop iteration with the same iterator, will result a single iteration of the last entry.

Command-line test case

#include <iostream>
#include <filesystem>

int main()
{
	std::filesystem::directory_iterator dir_iterator(".\\test");
	
	std::cout << "First iteration:" << std::endl;
	for (auto&& entry : dir_iterator)
	{
		std::cout << entry.path() << std::endl;
	}

	std::cout << "Second iteration:" << std::endl;
	for (auto&& entry : dir_iterator)
	{
		std::cout << entry.path() << std::endl;
	}	

        std::cout << "Done." << std::endl;

	return 0;
}

Actual behavior
Assuming that "test" directory, contains 3 files: file1.txt, file2.txt, file3.txt.
Output:

First iteration:
".\\test\\file1.txt"
".\\test\\file2.txt"
".\\test\\file3.txt"
Second iteration:
".\\test\\file3.txt"
Done

Expected behavior
I expect that the second iteration won't happen, since the dir_iterator should be equal to end.
Output:

First iteration:
".\\test\\file1.txt"
".\\test\\file2.txt"
".\\test\\file3.txt"
Second iteration:
Done

STL version
Microsoft Visual Studio Community 2019 Preview Version 16.5.0 Preview 5.0

Why it happens?
When a directory iterator is copied (default copy ctor), the member _Impl which
is a shared_ptr to the data (_Dir_enum_impl) of the current (iterated) entry, is also copied.
When the copy of the iterator reaches the end, it resets _Impl, so it is equal to end.
The original iterator, doesn't reset, hence, still has the _Impl which points to the data of the last entry,
which explains why a second iteration of the same iterator (which is copied by using begin),
starts the iteration at the last entry.

Additional thoughts
I'm not totally sure if this bug is just a really surprising/undefined behavior,
but I observed in other compiler (gcc), that this part of code other there
replaces the data of the last entry with just an empty entry, which maybe considered better,
yet unsatisfying.

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis issue is incorrect or by design

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions