1

I was using the experimental std::filesystem implementation in gcc 6.3.1, and ran into some very unexpected behavior with regard to std::experimental::filesystem::directory_iterator and std::distance. Specifically, after a call to std::distance, the original iterator appeared to have been modified.

After a bunch of fruitless debugging trying to find a logical error in my code, I started digging around in the implementation of directory_iterator, and finally found that the iterator uses std::shared_ptr internally, has a default copy-constructor, and I assume that operator++ must be directly incrementing the managed pointer.

The following code reproduces the issue:

#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;

int main() {
  auto it = fs::directory_iterator("/etc");
  std::cout << *it << std::endl;
  std::distance(it, fs::directory_iterator{});
  std::cout << *it << std::endl;
}

As this kind of implementation yields wildly counter-intuitive results whenever passing to a function that expects iterators with traditional value-semantics (all of the STL algorithms, as far as I know), I have a hard time believing this is intended behavior, but am hesitant to call a bug.

Obviously, this API was experimental at the time of release, but I was thinking it was intended to be a faithful implementation of the filesystem TS. I don't have access to a compiler with full C++17 support, so I had been hoping to use this in the intervening time.

Is this intended behavior, and should I expect directory_iterator to work this way in future releases? For the time being I guess I can use boost::filesystem.

Thanks!

Chris Fretz
  • 387
  • 1
  • 2
  • 10

1 Answers1

4

directory_iterators are defined to be InputIterators and therefore can only be used once. There is no const addition operator so you have to use the increment operator which modifies the iterator.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • I thought I understood the distinction between input and bidirectional iterators, but I take your point. The bit I was missing was (emphasis mine): "once an InputIterator i has been incremented, ALL COPIES of its previous value may be invalidated." I assumed copy-constructed InputIterators could be safely advanced without touching other instances, but turns out that's a ForwardIterator. Thanks! – Chris Fretz Mar 13 '18 at 09:08
  • I assume this limitation is to allow simple implementation on top of the underlying OS directory listing APIs which often only allow forward iteration over their results. – Alan Birtles Mar 13 '18 at 09:25
  • I was actually just thinking the same thing. To my knowledge, opendir/readdir only allow for forward iteration. – Chris Fretz Mar 13 '18 at 09:27