3065. LWG 2989 missed that all path's other operators should be hidden friends as well

Section: 31.12.6.8 [fs.path.nonmember] Status: C++20 Submitter: Billy O'Neal III Opened: 2018-02-13 Last modified: 2021-02-25

Priority: 2

View all issues with C++20 status.

Discussion:

Consider the following program:

// See godbolt link
#include <assert.h>
#include <string>
#include <filesystem>

using namespace std;
using namespace std::filesystem;

int main() {
  bool b = L"a//b" == std::string("a/b");
  assert(b); // passes. What?!
  return b;
}

L"a" gets converted into a path, and the string gets converted into a path, and then those paths are compared for equality. But path equality comparison doesn't work anything like string equality comparison, leading to surprises.

path's other operators should be made hidden friends as well, so that one side or the other of a given operator is of type path before those conversions apply.

[2018-02-20, Priority set to 2 after mailing list discussion]

[2018-08-23 Batavia Issues processing]

Status to Tentatively Ready

[2018-11, Adopted in San Diego]

Proposed resolution:

This wording is relative to N4713.

All drafting notes from LWG 2989 apply here too.

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

    […]
    // 31.12.6.8 [fs.path.nonmember], path non-member functions
    void swap(path& lhs, path& rhs) noexcept;
    size_t hash_value(const path& p) noexcept;
    
    bool operator==(const path& lhs, const path& rhs) noexcept;
    bool operator!=(const path& lhs, const path& rhs) noexcept;
    bool operator< (const path& lhs, const path& rhs) noexcept;
    bool operator<=(const path& lhs, const path& rhs) noexcept;
    bool operator> (const path& lhs, const path& rhs) noexcept;
    bool operator>=(const path& lhs, const path& rhs) noexcept;
    
    path operator/ (const path& lhs, const path& rhs);
    […]
    
  2. Modify 31.12.6.8 [fs.path.nonmember] as indicated:

    […]
    friend bool operator< (const path& lhs, const path& rhs) noexcept;
    […]
    friend bool operator<=(const path& lhs, const path& rhs) noexcept;
    […]
    friend bool operator> (const path& lhs, const path& rhs) noexcept;
    […]
    friend bool operator>=(const path& lhs, const path& rhs) noexcept;
    […]
    friend bool operator==(const path& lhs, const path& rhs) noexcept;
    […]
    friend bool operator!=(const path& lhs, const path& rhs) noexcept;
    […]
    friend path operator/ (const path& lhs, const path& rhs);
    […]
    
  3. Modify 31.12.6 [fs.class.path], class path synopsis, as indicated:

    class path {
    public:
      […]
      // 31.12.6.5.5 [fs.path.modifiers], modifiers
      […]
      
      // 31.12.6.8 [fs.path.nonmember], non-member operators
      friend bool operator< (const path& lhs, const path& rhs) noexcept;
      friend bool operator<=(const path& lhs, const path& rhs) noexcept;
      friend bool operator> (const path& lhs, const path& rhs) noexcept;
      friend bool operator>=(const path& lhs, const path& rhs) noexcept;
      friend bool operator==(const path& lhs, const path& rhs) noexcept;
      friend bool operator!=(const path& lhs, const path& rhs) noexcept;
      
      friend path operator/ (const path& lhs, const path& rhs);
      
      // 31.12.6.5.6 [fs.path.native.obs], native format observers
      […]
    };