Title
(recursive_)directory_iterator construction and traversal should not be noexcept
Status
c++20
Section
[fs.dir.itr.members][fs.rec.dir.itr.members][fs.op.copy] [fs.op.is.empty]
Submitter
Tim Song

Created on 2017-08-23.00:00:00 last changed 38 months ago

Messages

Date: 2018-03-18.16:03:30

Proposed resolution:

This wording is relative to N4700.

  1. 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;
    
  2. Edit [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 [fs.err.report].

    -4- [Note: […] — end note]

  3. Edit [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 [fs.err.report].

  4. Edit [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 [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 [fs.err.report].

    -5- [Note: […] — end note]

    -6- [Note: […] — end note]

  6. Edit [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 [fs.err.report].

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

Date: 2018-03-17.00:00:00

[ 2018-3-17 Adopted in Jacksonville ]

Date: 2017-11-03.00:00:00

[ 2017-11-03 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Date: 2017-08-23.00:00:00

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.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2018-03-18 16:03:30adminsetmessages: + msg9740
2018-03-18 16:03:30adminsetstatus: voting -> wp
2018-02-12 01:13:49adminsetstatus: ready -> voting
2017-11-03 17:13:13adminsetmessages: + msg9502
2017-11-03 17:13:13adminsetstatus: new -> ready
2017-08-27 13:31:35adminsetmessages: + msg9451
2017-08-23 00:00:00admincreate