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

### 3013. `(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], 99 [fs.op.is_empty] Status: C++20 Submitter: Tim Song Opened: 2017-08-23 Last modified: 2021-02-25

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.

Similarly, `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.

1. Edit 99 [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;

2. 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]

3. 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].

4. 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;

5. 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]

6. 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].

7. 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;

   >       […]

   >     }

8. 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]

9. Edit 99 [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: […]