我应该如何使 std::filesystem 看起来符合 Visual Studio 2015 标准

How should I make std::filesystem appear standards-conforming to Visual Studio 2015

我有一个项目目前锁定在 Visual Studio 2015 年。但是,我想编写尽可能符合标准的代码。

我想使用 std::filesystem 但它直到 C++-17 才成为标准。幸运的是,几乎所有东西都可用,就在 std::experimental::filesystem::v1 命名空间中。我不喜欢一揽子 using 指令;我更喜欢全面地界定事物的范围,以明确事物的来源。所以我不会只放入一个全局 using 语句。需要一些魔法来说服编译器做我想做的事。

这是我的第一次尝试:

#include <filesystem>
#if defined(_MSC_VER) && _MSC_VER <= 1900 // VS 2015
namespace std {
    namespace filesystem {
        using path = std::experimental::filesystem::v1::path;
    }
}
#endif

效果很好,std::filesystem::path 现在可以访问了。我已经测试了创建和使用 path 对象并且它有效。

随着我的前进,我知道我需要更多的东西。我想知道是否有办法将整个东西引入:

namespace std {
    namespace filesystem {
        using std::experimental::filesystem::v1;
    }
}

这似乎是一种倒退。似乎什么都看不见。事后看来,我想这是有道理的,因为 using 语句的范围以下一行的右大括号结束。

接下来,我想要一个directory_entry。同样的技术似乎有效

namespace std {
    namespace filesystem {
        using directory_entry = std::experimental::filesystem::v1::directory_entry;
    }
}

再一次,编译器似乎很高兴。

现在,我想使用 std::directory::create_directories。但是,这是一个函数,而不是 class,所以同样的技术不会奏效。

我认为 std::function 可能是为此量身定做的,但我没有任何运气。我试过了

namespace std {
    namespace filesystem {
        function<bool(path)> create_directories = std::experimental::filesystem::v1::create_directories;
    }
}

编译器说

Error   C2440   'initializing': cannot convert from 'overloaded-function' to 'std::function<bool (std::filesystem::path)>'

该函数有两个重载(一个将第二个参数作为 return 错误代码而不是抛出异常)。

我卡住了。这必须是可能的,但我的 C++-foo 很弱。

答案在于错误消息,以及为重载方法实例化 std::function 所需的帮助。感谢 MFisherKDX for pointing me and to W.F. 提供有效的答案。

忽略扩展标准命名空间是否合法、道德或是否符合品味的问题(因为在这种情况下,我相信它至少是 3 个中的 2 个),这是我完整评论的解决方法:

#if defined(_MSC_VER) && _MSC_VER <= 1900
// Visual Studio 2015 work-around ... 
// std::filesystem was incorporated into C++-17 (which is obviously after VS
// 2015 was released). However, Microsoft implemented the draft standard in
// the std::exerimental namespace. To avoid nasty ripple effects when the
// compiler is updated, make it look like the standard here
#include <functional>
namespace std {
  namespace filesystem {
    using directory_entry = std::experimental::filesystem::v1::directory_entry;
    using directory_iterator = std::experimental::filesystem::v1::directory_iterator;
    function<bool(path const&)> create_directories = 
        static_cast<bool(*)(path const&)>(
            std::experimental::filesystem::v1::create_directories);
  }
}
#endif

更新:Sebastian 有最简单的解决方案。

#if defined(_MSC_VER) && _MSC_VER <= 1900
// Visual Studio 2015 work-around ... 
// std::filesystem was incorporated into C++-17 (which is obviously after VS
// 2015 was released). However, Microsoft implemented the draft standard in
// the std::exerimental namespace. To avoid nasty ripple effects when the
// compiler is updated, make it look like the standard here
namespace std {
  namespace filesystem = experimental::filesystem::v1;
}
#endif

顺便说一下,gcc 7.3 需要几乎完全相同的解决方法,除了你不能

#include <filesystem>

但必须

#include <experimental/filesystem>

改为