Reputation: 2223
It is possible to append multiple paths in a row using the /
operator:
std::filesystem::path p1{"A"};
auto p2 = p1 / "B" / "C";
which is rather convenient. However, concat only offers +=
:
std::filesystem::path p1{"A"};
auto p2 = p1 / "B" / "C" + ".d"; // NOT OK
This is pretty annoying, as I can't easily add extensions to the end of my paths. I have no choice but to write something like
std::filesystem::path p1{"A"};
auto p2 = p1 / "B" / "C";
p2 += ".d";
Am I missing something? Is there a reason for this inconsistency?
Upvotes: 13
Views: 2679
Reputation: 25526
There's no such operator, but actually it's pretty easy to add such one on one's own; below is the quick and dirty variant coming along as a template (safer might be individual overloads matching those of operator+=
, not going that fare here, though):
template <typename T>
std::filesystem::path operator+(std::filesystem::path path, T&& data)
// (accepting by value, we're going to modify!)
{
path += std::forward<T>(data);
return path;
}
Sure, no perfect forwarding necessary either, all operators of path
accept by value or const reference anyway, but who knows if that changes any day…
If that's a good idea (see n314159 suspecting it might have been avoided by C++ committee to prevent confusing it with the other operator) – well, abstaining from reasoning about…
Upvotes: 2
Reputation: 2460
If all what you need is to add extensions, you can also do this:
std::filesystem::path p1{"A"};
auto p2 = (p1 / "B" / "C").replace_extension(".d");
Upvotes: 1
Reputation: 305
I though maybe it was because std::string and const char * implicitly convert to std::filesystem::path. Here is some c++20 being very careful to avoid unwanted implicit conversions.
#include <concepts>
#include <filesystem>
#include <iostream>
#include <string>
using namespace std::string_literals;
template <typename T, typename U>
requires std::same_as<std::decay_t<T>, std::filesystem::path> &&
(!std::same_as<std::decay_t<U>, std::filesystem::path>) &&
std::convertible_to<std::decay_t<U>, std::filesystem::path>
inline std::filesystem::path
operator+(const T &lhs, const U &rhs) {
auto tmp = lhs;
tmp += rhs;
return tmp;
}
template <typename T>
requires std::same_as<std::decay_t<T>, std::filesystem::path> ||
std::same_as<std::decay_t<T>, std::filesystem::directory_entry>
inline std::filesystem::path operator+(const std::filesystem::path &lhs,
const T &rhs) {
auto tmp = lhs;
tmp += rhs;
return tmp;
}
int main() {
std::cout << "aaa" + std::filesystem::path{"bbb"} << '\n';
std::cout << "aaa"s + std::filesystem::path{"bbb"} << '\n';
std::cout << std::filesystem::directory_entry{"aaa"} +
std::filesystem::path{"bbb"}
<< '\n';
std::cout << std::filesystem::path{"aaa"} + "bbb" << '\n';
std::cout << std::filesystem::path{"aaa"} + "bbb"s << '\n';
std::cout << std::filesystem::path{"aaa"} +
std::filesystem::directory_entry{"bbb"}
<< '\n';
std::cout << std::filesystem::path{"aaa"} + std::filesystem::path{"bbb"}
<< '\n';
std::cout << std::filesystem::directory_entry{"aaa"} +
std::filesystem::directory_entry{"bbb"}
<< '\n';
}
Upvotes: 0
Reputation: 5085
This is a bit speculative, but I think the reason for this is that an operator+
could be easily confused with operator/
. This would then lead to bugs if used as follows
path p2{"/"};
auto p1 = p2 + "A" + "B";
// Wants /A/B, gets /AB
Using string literals makes the workaround nicer:
using namespace std::literals;
std::filesystem::path p1{"A"};
auto p2 = p1 / "B" / ("C"s + ".d");
Here, "C"s
creates a std::string
with content C
and then we use std::string
's operator+
. If the "C"
part is itself already a path (otherwise you could just write "C.d"
to begin with), you can do
std::filesystem::path p1{"A"}, c_path{"C"};
auto p2 = p1 / "B" / (c_path += ".d");
since operator+=
returns the resulting object. (This is a bit wasteful but I can imagine that the compiler will optimize that).
Upvotes: 4