1

GCC (10.0.1) 和 Clang (11.0.0)/MSVC (VS 16.4.3) 当一个不存在的带有斜杠的路径作为参数给出时,表现出关于 std::filesystem::create_directories() 的不同行为。

更准确地说,虽然所有三个编译器都确实创建了目录,但后两个在这种情况下返回 false,使得 std::filesystem::create_directories() 的返回值模棱两可(并且违反直觉)。

具体来说,如果路径“a/b/c”中不存在文件,则以下程序,

#include <filesystem>
#include <iostream>

int main() {
    std::cout << std::boolalpha << std::filesystem::create_directories("a/b/c/");
}

在该路径创建一个目录,但在 Clang/MSVC 下打印 false 而在 GCC 下打印 true。

哪个是正确的行为?

4

1 回答 1

0

我可以确认,这不是先决条件问题,而且我认为 GCC 是正确的,但我的猜测是,在这一点上澄清标准会有所帮助。

长期以来,使用尾随分隔符标记目录是很常见的使用模式,以将它们与文件区分开来。如果要创建的路径在调用之前不存在,并且由于调用而在调用之后存在,我希望实现std::filesystem::create_directories返回 true。

让我们稍微扩展一下这个例子:

#include <filesystem>
#include <iostream>
#include <system_error>

int main() {
    std::error_code ec;
    std::cout << std::boolalpha << std::filesystem::exists("a/b/c/") << "/";
    std::cout << std::filesystem::create_directories("a/b/c/", ec) << "/";
    std::cout << !!ec << "/" << std::filesystem::exists("a/b/c/");
}

现在我们可以使用godbolt了:https ://godbolt.org/z/5883uY

正如我们所期望的那样,它显示为 GCC/libstdc++ false/true/false/true,它不存在,它是由create_directories调用创建的,没有错误,之后它就存在了。

现在 clang/libc++ 结果false/false/false/true,所以它不存在,它告诉我们它没有创建它,但是它确实创建了它,没有错误,我们在调用后证明了。

查看 MSVC stl 的 github 版本,它看起来像调用CreateDirectoryWaa\ba\b\c和 finally a\b\c\,最后一个报告ERROR_ALREADY_EXISTS,这导致false我们似乎没有做任何事情的返回,但我认为这是一个实现细节渗出功能。我的猜测是,Clang 代码的工作方式类似,但我还没有检查 libc++ 源代码。

标准内容如下:“返回:如果创建了新目录,则返回 true,否则返回 false。如果发生错误,带有参数 ec 的签名将返回 false。”

因此,标准想要创建一个目录true,而不是最后一个目录,并且对于ec变体发出错误信号,但 clang 告诉我们没有。falseec

我可能仍然是错的,因为我不在任何 WG 中,但我的结论是:GCC 是正确的,它似乎是 MSVC 和 Clang 中的一个错误。

因为我在验证我自己的实现时已经收集了实现之间的差异,所以如果可以的话,我会“高兴地”将这个添加到我的集合中。

更新:我刚刚挖掘了其他来源,Clang/libc++ 使用基于递归parent_path()的机制,最终导致与 MSVC 相同的序列,其中最后一个::mkdir("a/b/c/")前面是 a::mkdir("a/b/c")所以它报告错误,因为它假设它没有创建目录。

于 2020-02-08T22:44:08.447 回答