std::filesystem::last_write_time 中的 g++ 重大变化

g++ breaking change in std::filesystem::last_write_time

随着最近的 OS 升级,我注意到我的一小部分代码停止了编译 - 似乎原因是从 g++-8 切换到 g++-9

此代码在 g++ 8.3.0 上编译正常(使用来自 Dockerhub 的 gcc:8.3 图像验证了这一点)

#include <filesystem>
#include <chrono>

int main() {
    namespace fs = std::filesystem;
    using namespace std::chrono_literals;
    std::filesystem::last_write_time("test", std::chrono::system_clock::now() - 5min);
}

g++ 9.1.0 上它失败了:

test.cpp: In function 'int main()':
test.cpp:8:69: error: no matching function for call to 'last_write_time(const char [5], std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >)'
    8 |  fs::last_write_time("test", std::chrono::system_clock::now() - 5min);
      |                                                                     ^
In file included from /usr/local/include/c++/9.1.0/filesystem:39,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_ops.h:243:19: note: candidate: 'std::filesystem::file_time_type std::filesystem::last_write_time(const std::filesystem::__cxx11::path&)'
  243 |   file_time_type  last_write_time(const path& __p);
      |                   ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:243:19: note:   candidate expects 1 argument, 2 provided
/usr/local/include/c++/9.1.0/bits/fs_ops.h:244:19: note: candidate: 'std::filesystem::file_time_type std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::error_code&)'
  244 |   file_time_type  last_write_time(const path& __p, error_code& __ec) noexcept;
      |                   ^~~~~~~~~~~~~~~
In file included from /usr/local/include/c++/9.1.0/filesystem:36,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_fwd.h:362:47: note:   no known conversion for argument 2 from 'std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >' to 'std::error_code&'
  362 |   file_time_type last_write_time(const path&, error_code&) noexcept;
      |                                               ^~~~~~~~~~~
In file included from /usr/local/include/c++/9.1.0/filesystem:39,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_ops.h:245:8: note: candidate: 'void std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::filesystem::file_time_type)'
  245 |   void last_write_time(const path& __p, file_time_type __new_time);
      |        ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:245:56: note:   no known conversion for argument 2 from 'time_point<std::chrono::_V2::system_clock,[...]>' to 'time_point<std::filesystem::__file_clock,[...]>'
  245 |   void last_write_time(const path& __p, file_time_type __new_time);
      |                                         ~~~~~~~~~~~~~~~^~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:246:8: note: candidate: 'void std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::filesystem::file_time_type, std::error_code&)'
  246 |   void last_write_time(const path& __p, file_time_type __new_time,
      |        ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:246:8: note:   candidate expects 3 arguments, 2 provided

shell returned 1

Press ENTER or type command to continue

编译命令:g++ -std=c++17 test.cpp -lstdc++-fs(即使在 g++9 之后不需要链接 stdc++-fs

我的问题是 - 按照我的意图,这个函数的惯用用法是什么?即把一个文件的最后写入时间改为五分钟前。

我知道如果引入了重大更改,我会以某种非惯用的方式使用它。

cppreference.com's example 提到了这一点。

你给 std::chrono::time_point<std::chrono::system_clock>std::file_time_type 从来都不可移植;它恰好适用于您以前的工具链。那真的不是你的错:你不可避免地依赖于实现细节。

C++20 引入了一种可移植的替代方案,因此您可以:

std::filesystem::last_write_time("test", std::chrono::file_clock::now() - 5min);
//                                                    ^^^^^^^^^^

…即使用file_clock,而不是仅仅希望替代方案可以转换为您的工具链使用的任何implementation-defined时钟。

GCC 9 是否支持它,但我不能确定 it doesn't look like it。如果有解决方法,我不知道有一个(这就是进行 C++20 更改的原因)。

This problem affects other people on other toolchains,也是。

正如@LightnessRacesinOrbit 在他们的回答中指出的那样,last_write_time 采用的 std::filesystem::file_time_type 使用未指定的 time_point 类型。这意味着从一个编译器转移到另一个甚至同一编译器的不同版本是完全合法的。

您可以做的是获取实施使用的时钟,然后自己使用。 std::chrono::time_point 的构建是为了将创建它的时钟类型作为模板参数,并且它显示了一个 public clock 类型来表示它。因此,要便携地获取时钟,并在其上调用 now,您可以使用

std::filesystem::last_write_time("test", std::filesystem::file_time_type::clock::now() - 5min);
//                                       ^ give me whatever clock you use and call now on it