This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3480. `directory_iterator` and `recursive_directory_iterator` are not C++20 ranges

Section: 31.12.11 [fs.class.directory.iterator], 31.12.12 [fs.class.rec.dir.itr] Status: WP Submitter: Barry Revzin Opened: 2020-08-27 Last modified: 2021-10-14

Priority: 3

View all other issues in [fs.class.directory.iterator].

View all issues with WP status.

Discussion:

`std::filesystem::directory_iterator` and `std::filesystem::recursive_directory_iterator` are intended to be ranges, but both fail to satisfy the concept `std::ranges::range`.

They both opt in to being a range the same way, via non-member functions:

>     directory_iterator begin(directory_iterator iter) noexcept;
>     directory_iterator end(const directory_iterator&) noexcept;

>     recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
>     recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;

This is good enough for a range-based for statement, but for the `range` concept, non-member `end` is looked up in a context that includes (26.3.3 [range.access.end]/2.6) the declarations:

>     void end(auto&) = delete;
>     void end(const auto&) = delete;

Which means that non-`const` `directory_iterator` and non-`const` `recursive_directory_iterator`, the `void end(auto&)` overload ends up being a better match and thus the CPO `ranges::end` doesn't find a candidate. Which means that `{recursive_,}directory_iterator` is not a range, even though `const {recursive_,}directory_iterator` is a range.

This could be fixed by having the non-member `end` for both of these types just take by value (as libstdc++ currently does anyway) or by adding member functions `begin() const` and `end() const`.

A broader direction would be to consider removing the poison pill overloads. Their motivation from P0970 was to support what are now called borrowed ranges — but that design now is based on specializing a variable template instead of providing a non-member `begin` that takes an rvalue, so the initial motivation simply no longer exists. And, in this particular case, causes harm.

[2020-09-06; Reflector prioritization]

Set priority to 3 during reflector discussions.

[2021-02-22, Barry Revzin comments]

When we do make whichever of the alternative adjustments necessary such that `range<directory_iterator>` is `true`, we should also remember to specialize `enable_borrowed_range` for both types to be `true` (since the iterator is the range, this is kind of trivially true).

[2021-05-17, Tim provides wording]

Both MSVC and libstdc++'s `end` already take its argument by value, so the wording below just does that. Any discussion about changing or removing the poison pills is probably better suited for a paper.

[2021-06-23; Reflector poll]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

1. Edit 31.12.4 [fs.filesystem.syn], header `<filesystem>` synopsis, as indicated:

   >     […]
   >     namespace std::filesystem {
   >       […]

   >       // 31.12.11.3 [fs.dir.itr.nonmembers], range access for directory iterators
   >       directory_iterator begin(directory_iterator iter) noexcept;
   >       directory_iterator end(const directory_iterator&) noexcept;

   >       […]

   >       // 31.12.12.3 [fs.rec.dir.itr.nonmembers], range access for recursive directory iterators
   >       recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
   >       recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;

   >       […]
   >     }

   >     namespace std::ranges {
   >       template<>
   >       inline constexpr bool enable_borrowed_range<filesystem::directory_iterator> = true;
   >       template<>
   >       inline constexpr bool enable_borrowed_range<filesystem::recursive_directory_iterator> = true;

   >       template<>
   >       inline constexpr bool enable_view<filesystem::directory_iterator> = true;
   >       template<>
   >       inline constexpr bool enable_view<filesystem::recursive_directory_iterator> = true;
   >     }

2. Edit 31.12.11.3 [fs.dir.itr.nonmembers] as indicated:

   > -1- These functions enable range access for `directory_iterator`.

   >     directory_iterator begin(directory_iterator iter) noexcept;

   > > -2- Returns: `iter`.

   >     directory_iterator end(const directory_iterator&) noexcept;

   > > -3- Returns: `directory_iterator()`.

3. Edit 31.12.12.3 [fs.rec.dir.itr.nonmembers] as indicated:

   > -1- These functions enable use of `recursive_directory_iterator` with range-based for statements.

   >     recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;

   > > -2- Returns: `iter`.

   >     recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;

   > > -3- Returns: `recursive_directory_iterator()`.