I have the following code to normalize std::filesystem::path to always use forward slashes as separators:
static decltype(auto) fix_path_separator(const std::filesystem::path& path) {
if constexpr (std::filesystem::path::preferred_separator == '/') {
return path;
} else {
return std::filesystem::path{ path.generic_string() };
}
}
I use decltype(auto) because on POSIX systems I want this function to be noop and return argument as a reference without creating a copy, but for Windows, I need a copy to modify the separators. This code appears to work and I have static_assert'ed that it indeed returns const path& on POSIX and path on Windows, but am not exactly sure how it is deduced.
I can understand the POSIX path - I am returning a variable declared as const path&, so decltype(auto) deduces const path&. But for Windows, it looks to me that I am returning a path&& as an unnamed temporary, not a path.
Can someone please provide me the decltype(auto) deduction rules which apply here?
Here is a godbolt example showcasing equivalent code.
The return type of a function declared with decltype(auto) is deduced by substituting the operands of its (non-discarded) return statements for auto ([dcl.type.auto.deduct]/4).
In the true branch of the constexpr-if statement, this produces decltype(path), which falls under [dcl.type.decltype]/1.3 (unparenthesized id-expression naming a variable) and yields the declared type of path, namely const std::filesystem::path&.
In the other branch, the operand of decltype would be the expression std::filesystem::path { /* … */ }, which is a prvalue of type std::filesystem::path ([expr.type.conv]/1.4). This case falls under [dcl.type.decltype]/1.7, and so the resulting type is simply the type of the expression. (An rvalue reference type would be produced if the expression were an xvalue instead of a prvalue, as per [dcl.type.decltype]/1.5.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With