关于std::filesystem::path的/运算符特殊行为的原因咨询
/ Operator on Windows Drive-Like Paths Let’s break down what’s happening here—this behavior isn’t a bug, it’s intentional, and tied directly to Windows’ legacy path semantics that std::filesystem strictly adheres to.
The Core Windows Path Distinction You’re Missing
First, you need to know Windows treats these two path formats as fundamentally different:
c:\is an absolute path: it explicitly points to the root directory of the C drive, no exceptions.c:is a drive-relative path: it refers to the current working directory of the C drive (a holdover from DOS days, where each drive maintains its own independent working directory). For example, if your process’s C drive working directory isC:\SRC\ConsoleApplication3\, thenc:test.logresolves toC:\SRC\ConsoleApplication3\test.log—not the root directory where your file lives.
Why std::filesystem Acts This Way
The / operator (the append operation) in std::filesystem is designed to mirror the platform’s native path behavior perfectly. So:
- When appending to an absolute path like
c:\\, it correctly adds the filename to the root directory, resulting inc:\test.log. - When appending to a drive-relative path like
c:, it just concatenates the filename directly (sincec:already implies "current directory on C drive"), givingc:test.log. This is exactly what Windows expects, but it’s counterintuitive if you don’t know about drive-specific working directories.
Your CreateFile call fails because c:test.log is looking for the file in your process’s C drive working directory, not the root—where your test.log actually resides when the c:\\ case works.
Fixing the Issue (Since You Can’t Control Callers)
If you need to ensure drive-relative paths like c: resolve to the drive’s root directory instead of the current working directory, normalize the input path before appending the filename. Here are two practical approaches:
1. Force the Path to Use the Drive Root
Check if the path has a drive name but no root directory, then explicitly add the root:
#include <filesystem> #include <Windows.h> namespace fs = std::filesystem; HANDLE CreateHandleFromPath(const fs::path& path, const std::string& fileName) { fs::path normalized_path = path; // Detect drive-relative paths (e.g., "c:", "d:") if (normalized_path.has_root_name() && !normalized_path.has_root_directory()) { // Convert to the drive's root path (e.g., "c:\", "d:\") normalized_path = normalized_path.root_name() / fs::path("/"); } auto path_complete = normalized_path / fileName; HANDLE hFile = CreateFileW( path_complete.wstring().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile == INVALID_HANDLE_VALUE) { throw fs::filesystem_error( "Can't get handle", std::error_code(::GetLastError(), std::system_category())); } return hFile; }
2. Resolve to an Absolute Path (If You Want the Current Working Directory)
If instead you want c: to resolve to the actual current working directory on the C drive, use fs::absolute() to convert the path to a full absolute path before appending:
auto path_complete = fs::absolute(path) / fileName;
This will turn c: into C:\SRC\ConsoleApplication3\, so path_complete becomes C:\SRC\ConsoleApplication3\test.log—use this only if that’s the directory you intend to target.
内容的提问来源于stack exchange,提问作者usurpbrain

