(recursive_)directory_iterator
construction and traversal should not be noexcept
Section: 31.12.11.2 [fs.dir.itr.members], 31.12.12.2 [fs.rec.dir.itr.members], 31.12.13.4 [fs.op.copy], 31.12.13.20 [fs.op.is.empty] Status: C++20 Submitter: Tim Song Opened: 2017-08-23 Last modified: 2023-02-07
Priority: 0
View all other issues in [fs.dir.itr.members].
View all issues with C++20 status.
Discussion:
Constructing a (recursive_)directory_iterator
from a path
requires, at a minimum, initializing its
underlying directory_entry
object with the path
formed from the supplied path
and the name of the
first entry, which requires a potentially throwing memory allocation; every implementation I've looked at also allocates
memory to store additional data as well.
increment()
needs to update the path
stored in directory_entry
object to refer
to the name of the next entry, which may require a memory allocation. While it might conceivably be possible to
postpone the update in this case until the iterator is dereferenced (the dereference operation is not noexcept
due to its narrow contract), it seems highly unlikely that such an implementation is intended (not to mention that it
would require additional synchronization as the dereference operations are const).
This further calls into question whether the error_code
overloads of copy
and is_empty
,
whose specification uses directory_iterator
, should be noexcept
. There might be a case for keeping
the noexcept
for is_empty
, although that would require changes in all implementations I checked
(libstdc++, libc++, and Boost). copy
appears to be relentlessly hostile to noexcept
, since its
specification forms a path
via operator/
in two places (bullets 4.7.4 and 4.8.2) in addition to the
directory_iterator
usage. The proposed resolution below removes both.
[ 2017-11-03 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]
[2018-3-17 Adopted in Jacksonville]
Proposed resolution:
This wording is relative to N4700.
Edit [fs.class.directory_iterator], class directory_iterator
synopsis, as indicated:
[…] explicit directory_iterator(const path& p); directory_iterator(const path& p, directory_options options); directory_iterator(const path& p, error_code& ec)noexcept; directory_iterator(const path& p, directory_options options, error_code& ec)noexcept; […] directory_iterator& operator++(); directory_iterator& increment(error_code& ec)noexcept;
Edit 31.12.11.2 [fs.dir.itr.members] before p2 as indicated:
explicit directory_iterator(const path& p); directory_iterator(const path& p, directory_options options); directory_iterator(const path& p, error_code& ec)noexcept; directory_iterator(const path& p, directory_options options, error_code& ec)noexcept;-2- Effects: […]
-3- Throws: As specified in 31.12.5 [fs.err.report]. -4- [Note: […] — end note]
Edit 31.12.11.2 [fs.dir.itr.members] before p10 as indicated:
directory_iterator& operator++(); directory_iterator& increment(error_code& ec)noexcept;-10- Effects: As specified for the prefix increment operation of Input iterators (24.2.3).
-11- Returns:*this
. -12- Throws: As specified in 31.12.5 [fs.err.report].
Edit 31.12.12 [fs.class.rec.dir.itr], class recursive_directory_iterator
synopsis, as indicated:
[…] explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec)noexcept; recursive_directory_iterator(const path& p, error_code& ec)noexcept; […] recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec)noexcept;
Edit 31.12.12.2 [fs.rec.dir.itr.members] before p2 as indicated:
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec)noexcept; recursive_directory_iterator(const path& p, error_code& ec)noexcept;-2- Effects: […]
-3- Postconditions: […] -4- Throws: As specified in 31.12.5 [fs.err.report]. -5- [Note: […] — end note] -6- [Note: […] — end note]
Edit 31.12.12.2 [fs.rec.dir.itr.members] before p23 as indicated:
recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec)noexcept;-23- Effects: As specified for the prefix increment operation of Input iterators (24.2.3), except that: […]
-24- Returns:*this
. -25- Throws: As specified 31.12.5 [fs.err.report].
Edit 31.12.4 [fs.filesystem.syn], header <filesystem>
synopsis, as indicated:
namespace std::filesystem { […] void copy(const path& from, const path& to); void copy(const path& from, const path& to, error_code& ec)noexcept; void copy(const path& from, const path& to, copy_options options); void copy(const path& from, const path& to, copy_options options, error_code& ec)noexcept; […] bool is_empty(const path& p); bool is_empty(const path& p, error_code& ec)noexcept; […] }
Edit 31.12.13.4 [fs.op.copy] as indicated:
void copy(const path& from, const path& to, error_code& ec)noexcept;-2- Effects: Equivalent to
copy(from, to, copy_options::none, ec)
.void copy(const path& from, const path& to, copy_options options); void copy(const path& from, const path& to, copy_options options, error_code& ec)noexcept;-3- Requires: […]
-4- Effects: […] -5- Throws: […] -6- Remarks: […] -7- [Example: […] — end example]
Edit [fs.op.is_empty] as indicated:
bool is_empty(const path& p); bool is_empty(const path& p, error_code& ec)noexcept;-1- Effects: […]
-2- Throws: […]