如果p以根路径开头,为什么std::filesystem::path::append会替换当前路径

Why does std::filesystem::path::append replace the current path if p starts with root path

给出以下代码:

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path fsBase = "/base";
    fs::path fsAppend = "/append";
    auto fsResult = fsBase / fsAppend;

    std::cout << "fsResult: " << fsResult << std::endl;
    return 0;
}

通常期望的结果是/base/append,但实际上给出的是/append.

fs::path::append 的描述确实表明了这种行为:

If p.is_absolute() || (p.has_root_name() && p.root_name() != root_name()), then replaces the current path with p as if by operator=(p) and finishes.

然而,std::experimental::filesystemboost::filesystem 的行为不同,这给出了预期的 /base/append。 参见 examples

问题是为什么会这样?为什么用append()函数替换路径?

fsAppend 是绝对路径,因为它以 / 开头,并且您在 POSIX 等系统上,其中以 / 开头的路径是绝对路径。

将一个绝对路径附加到另一个绝对路径没有任何意义(对我来说抛出异常实际上是最自然的结果)。 C:\foo.txt append C:\bar.txt 的结果应该是什么?

experimental::fs 中,规则是如果第二个参数的 .native() 以目录分隔符开头,那么它会被视为附加目的的相对路径,即使它可能是绝对路径其他情况!

标准化的文件系统明确区分了绝对路径和相对路径,试图避免在 POSIX 系统上出现的这种歧义。

可以在 P0492R2 US77 中找到更改的记录。

请注意,您可以使用 += 而不是 / 进行连接(应该如您所愿),或者在使用 /.[=23= 之前使第二个参数成为相对的]

另请参阅 以进一步比较 experimental 和最终确定。

文件名不是(道德上的)字符串:在结构上附加路径 a 和相对路径 b 回答问题

If a were the current directory, what would the path b mean?

首先,如果a 当前工作目录,这是相对的→绝对 函数(虽然 filesystem::absolute 做的多一点,因为 Windows D:foo 既不完全相对也不完全绝对)。

例如考虑#include"…"的行为:如果文件名是相对的,则首先考虑从包含带有#include的文件的目录开始,然后考虑从来自包含路径的每个元素(例如-I/start/here)。其中每一个都可以表述为提出上述问题:

void handle_include(const std::filesystem::path &reading,
                    const std::filesystem::path &name,
                    const std::vector<std::filesystem::path> &srch) {
  std::ifstream in(reading.parent_path()/name);
  if(!in) {
    for(auto &s : srch) {
      in.open(s/name);
      if(in) break;
    }
    if(!in) throw …;
  }
  // use in
}

如果 name 绝对值例如#include"/usr/include/sys/stat.h")会怎样?唯一正确的答案是使用 name 而不用 考虑 readings。 (在这里,这会低效地多次考虑同一个文件,但这是效率问题,而不是正确性问题,并且只影响错误情况。)还要注意相关的 identity that a/b.lexically_proximate(a)==b; lexically_proximate 可以 return 绝对路径(当两个路径具有不同的根名称时),而 lexically_relative 可能会失败并丢失信息。

这种方法还避免了盲目连接在 Windows 上给出的毫无意义的无用答案:C:\foo\D:\bar 甚至不是一个有效的文件名,更不用说了一个任何人都可以通过组合它的部分来获得的东西。当然引发 exception 也可以避免这种情况,但是以阻止上述合理用例为代价。甚至还有 path("c:\foo\bar").append("\baz\quux") 的情况,它保留每个的 部分 并产生 path("c:\baz\quux"),这也是上述问题的正确答案。

鉴于没有人应该写这样的东西

[project]
  headers=/include
  manual=/doc

当这种解释不正确时,右侧操作数没有理由是绝对的。 (显然,如果是,可以写 base/cfg.relative_path();这是 的答案。)

该行为的灵感来自 Python 的 os.path.join,它依次对每个参数执行此操作。