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.

### 2936. Path comparison is defined in terms of the generic format

Section: 29.12.6.5.8 [fs.path.compare] Status: C++20 Submitter: Billy Robert O'Neal III Opened: 2017-02-21 Last modified: 2021-02-25

Priority: 2

View all issues with C++20 status.

Discussion:

Currently, path comparison is defined elementwise, which implies a conversion from the native format (implied by `native()` returning `const string&`). However, the conversion from the native format to the generic format might not be information preserving. This would allow two paths `a` and `b` to say `a.compare(b) == 0`, but `a.native().compare(b.native()) != 0` as a result of this missing information, which is undesirable. We only want that condition to happen if there are redundant directory separators. We also don't want to change the path comparison to be in terms of the native format, due to Peter Dimov's example where we want `path("a/b")` to sort earlier than `path("a.b")`, and we want `path("a/b") == path("a//////b")`.

Citing a Windows example, conversion to the generic format is going to have to drop alternate data streams. This might give `path("a/b:ads") == path("a/b")`. I think I should consider the alternate data streams as part of the path element though, so this case might be fine, so long as I make `path("b:ads").native()` be `"b:ads"`. This might not work for our z/OS friends though, or for folks where the native format looks nothing like the generic format.

Additionally, this treats root-directory specially. For example, the current spec wants `path("c:/a/b") == path("c:/a////b")`, but `path("c:/a/b") != path("c:///a/b")`, because `native()` for the root-directory path element will literally be the slashes or preferred separators.

This addresses similar issues to those raised in US 57 — it won't make absolute paths sort at the beginning or end but it will make paths of the same kind sort together.

[2017-03-04, Kona Saturday morning]

We decided that this had seen so much churn that we would postpone looking at this until Toronto

[2017-07 Toronto Thurs Issue Prioritization]

Priority 2

[2016-07, Toronto Saturday afternoon issues processing]

Billy to reword after Davis researches history about ordering. Status to Open.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4640.

> 1. Make the following edits to 29.12.6.5.8 [fs.path.compare]:

>    >     int compare(const path& p) const noexcept;

>    > > -1- Returns:

>    > > > — Let `rootNameComparison` be the result of `this->root_name().native().compare(p.root_name().native())`. If `rootNameComparison` is not `0`, `rootNameComparison`; otherwise,

>    > > > — If `this->has_root_directory()` and `!p.has_root_directory()`, a value less than `0`; otherwise,

>    > > > — If `!this->has_root_directory()` and `p.has_root_directory()`, a value greater than `0`; otherwise,

>    > > > — A value greater than, less than, or equal to 0, ordering the paths in a depth-first traversal order.

>    > > -?- [Note: For POSIX and Windows platforms, this is accomplished by lexicographically ordering the half-open ranges `[begin(), end())` of `this->relative_path()` and `p.relative_path()` as follows:

>    > > > — A value less than `0`, if `native()` for the elements of `*this->relative_path()` are lexicographically less than `native()` for the elements of `p.relative_path()`; otherwise,

>    > > > — a value greater than `0`, if `native()` for the elements of `*this->relative_path()` are lexicographically greater than `native()` for the elements of `p.relative_path()`; otherwise,

>    > > > — `0`.

>    > > — end note]

>    > > -2- Remarks: The elements are determined as if by iteration over the half-open range `[begin(), end())` for `*this` and `p`.

>    >     int compare(const string_type& s) const
>    >     int compare(basic_string_view<value_type> s) const;

>    > > -3- Returns: `compare(path(s))`

>    > > > [Editor's note: Delete paragraph 3 entirely and merge the `value_type` overload with those above.]

>    >     int compare(const value_type* s) const

>    > > -4- Returns Effects: Equivalent to `return` `compare(path(s));.`

[2018-01-26 issues processing telecon]

Status set to 'Review'. We like the wording, but would like to see some implementation experience.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4659.

> 1. Make the following edits to 29.12.6.5.8 [fs.path.compare]:

>    >     int compare(const path& p) const noexcept;

>    > > -1- Returns:

>    > > > — Let `rootNameComparison` be the result of `this->root_name().native().compare(p.root_name().native())`. If `rootNameComparison` is not `0`, `rootNameComparison`; otherwise,

>    > > > — If `this->has_root_directory()` and `!p.has_root_directory()`, a value less than `0`; otherwise,

>    > > > — If `!this->has_root_directory()` and `p.has_root_directory()`, a value greater than `0`; otherwise,

>    > > > — a value less than `0`, i If `native()` for the elements of `*this->relative_path()` are lexicographically less than `native()` for the elements of `p.relative_path()`, a value less than `0`; otherwise,

>    > > > — a value greater than `0`, i If `native()` for the elements of `*this->relative_path()` are lexicographically greater than `native()` for the elements of `p.relative_path()`, a value greater than `0`; otherwise,

>    > > > — `0`.

>    > > -2- Remarks: The elements are determined as if by iteration over the half-open range `[begin(), end())` for `*this` and `p`.

>    >     int compare(const string_type& s) const
>    >     int compare(basic_string_view<value_type> s) const;

>    > > -3- Returns: `compare(path(s))`

>    > > > [Editor's note: Delete paragraph 3 entirely and merge the `value_type` overload with those above.]

>    >     int compare(const value_type* s) const

>    > > -4- Returns Effects: Equivalent to `return` `compare(path(s));.`

[2018-02-13 Billy improves wording]

The revised wording has the effect to invert the ordering of the added new bullets (2) and (3), the effect of this change is that

>     path("c:/").compare("c:")

compares greater, not less.

[2018-06, Rapperswil Wednesday evening]

Agreement to move that to Ready, Daniel rebased to N4750.

[2018-11, Adopted in San Diego]

Proposed resolution:

This wording is relative to N4750.

1. Make the following edits to 29.12.6.5.8 [fs.path.compare]:

   >     int compare(const path& p) const noexcept;

   > > -1- Returns:

   > > > — Let `rootNameComparison` be the result of `this->root_name().native().compare(p.root_name().native())`. If `rootNameComparison` is not `0`, `rootNameComparison`; otherwise,

   > > > — If `!this->has_root_directory()` and `p.has_root_directory()`, a value less than `0`; otherwise,

   > > > — If `this->has_root_directory()` and `!p.has_root_directory()`, a value greater than `0`; otherwise,

   > > > — a value less than `0`, i If `native()` for the elements of `*this->relative_path()` are lexicographically less than `native()` for the elements of `p.relative_path()`, a value less than `0`; otherwise,

   > > > — a value greater than `0`, i If `native()` for the elements of `*this->relative_path()` are lexicographically greater than `native()` for the elements of `p.relative_path()`, a value greater than `0`; otherwise,

   > > > — `0`.

   > > -2- Remarks: The elements are determined as if by iteration over the half-open range `[begin(), end())` for `*this` and `p`.

   >     int compare(const string_type& s) const
   >     int compare(basic_string_view<value_type> s) const;

   > > -3- Returns: `compare(path(s))`

   > > > [Editor's note: Delete paragraph 3 entirely and merge the `value_type` overload with those above.]

   >     int compare(const value_type* s) const

   > > -4- Returns Effects: Equivalent to: `return` `compare(path(s));.`