directory_iterator
and recursive_directory_iterator
are not C++20 rangesSection: 31.12.11 [fs.class.directory.iterator], 31.12.12 [fs.class.rec.dir.itr] Status: C++23 Submitter: Barry Revzin Opened: 2020-08-27 Last modified: 2023-11-22
Priority: 3
View all other issues in [fs.class.directory.iterator].
View all issues with C++23 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
.
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 (25.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.
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.
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(constdirectory_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(constrecursive_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; }
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(constdirectory_iterator&) noexcept;-3- Returns:
directory_iterator()
.
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(constrecursive_directory_iterator&) noexcept;-3- Returns:
recursive_directory_iterator()
.