c++oding="utf-8" ?>
std::filesystem::copy_file默认不覆盖目标文件,必须显式传入std::filesystem::copy_options::overwrite_existing选项,否则抛出filesystem_error(错误码std::errc::file_exists);该枚举非位域,不可与其他选项组合,且需完整命名空间限定。

filesystem::copy_file 默认不覆盖,必须显式指定 copy_options::overwrite_existing
直接调用 std::filesystem::copy_file(src, dst) 时,如果 dst 已存在,会抛出 std::filesystem::filesystem_error,错误码通常是 std::errc::file_exists。这不是 bug,是标准行为 —— 它默认保守,拒绝静默覆盖。
要覆盖,必须传入第三个参数 copy_options 枚举值:
std::filesystem::copy_file("a.txt", "b.txt", std::filesystem::copy_options::overwrite_existing);
- 只写这个选项即可,无需组合其他 flag(比如不用加
skip_existing) - 如果目标是符号链接,
overwrite_existing会替换链接本身,不是链接指向的文件 - C++17 起支持;C++20 新增了
copy_options::update_existing(仅当源比目标新时覆盖),但不解决“强制覆盖”需求
常见错误:传错枚举值或漏掉命名空间
最容易踩的坑是写成 std::filesystem::overwrite_existing(少了个 copy_options:: 前缀),编译直接失败 —— 因为 overwrite_existing 不是 std::filesystem 的直接成员,而是嵌套在 copy_options 里的枚举项。
另一个典型错误是误用 copy_options::skip_existing,它完全跳过已存在文件,不会报错也不会覆盖,和需求相反。
- 正确写法只有这一种:
std::filesystem::copy_options::overwrite_existing - 不要试图用位运算组合(如
|),这个枚举不是 bitmask,overwrite_existing是独立值 - 某些旧版 libstdc++(如 GCC 8.3 之前)可能未完全实现该枚举,建议 GCC ≥9 或 Clang ≥9
覆盖时要注意权限和只读文件
即使指定了 overwrite_existing,如果目标文件存在且是只读的(例如 Windows 上设置了只读属性,或 Linux 上权限为 -r--r--r--),copy_file 仍可能失败,抛出 permission_denied。
这不是标准要求的行为差异,而是底层 OS API 的限制。C++ 标准不负责帮你改权限 —— 它只是调用 open() / CreateFile() 等系统调用。
- 安全做法:覆盖前先用
std::filesystem::permissions(dst, std::filesystem::perms::owner_write)尝试加写权限 - 但注意:修改权限本身也可能失败(如无权操作该目录),需捕获异常并处理
- Windows 下尤其常见:资源管理器勾选“只读”,
copy_file就会卡住
替代方案:先 remove 再 copy_file 更可控?
有人倾向手动 std::filesystem::remove(dst) 再 copy_file(src, dst),认为逻辑更清晰。这确实绕开了权限问题(因为 remove 成功后新建文件天然可写),但引入了新风险:
- 两次系统调用,中间若崩溃或被中断,
dst会丢失,而原src未动 —— 这是不可逆的数据丢失 - 多线程/多进程环境下,
remove和copy_file不是原子操作,竞态窗口更大 -
copy_file(..., overwrite_existing)在大多数实现中是原子的(至少对普通文件是),更安全
除非你明确需要自定义覆盖逻辑(比如备份旧文件、记录日志),否则优先用标准的 overwrite_existing。
真正容易被忽略的是:覆盖行为依赖于底层文件系统是否支持原子替换。在 NFS 或某些容器挂载卷上,overwrite_existing 可能退化为“删+写”,这时权限和竞态问题就又回来了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











