14

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?

8
  • 4
    Nope. There is no other way Commented Jan 27, 2021 at 11:07
  • @KamilCuk not possible with const char[N], that would be auto p2 = p1 / "B" / (std::string("C") + ".d"); Commented Jan 27, 2021 at 11:16
  • ("C" + ".d") adds two const char[2]s Commented Jan 27, 2021 at 11:17
  • @KamilCuk a string literal like "A" is of type "const char[N]" and will be converted to "const char*" when using operator +, so you're adding two pointers with "A"+"B" Commented Jan 27, 2021 at 11:17
  • 1
    sooo. auto p2 = p1 / "B" / "C" += ".d"; or auto p2 = p1 / "B" / (std::string() + "C" + ".d"); :D . Both of the these work in godbolt. note that in practice, "C" will most likely Not in practice, in any case the result of p1 / "B" / "C" has type std::filesystem::path, so it's doing += on it. Still, this is not an answer, as I do not know "the reason for this inconsistency". Commented Jan 27, 2021 at 11:21

4 Answers 4

4

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

Sign up to request clarification or add additional context in comments.

1 Comment

You can simply auto p2 = p1 / "B" / "C" += ".d"; No parentheses necessary due to operator precedence.
2

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…

Comments

1

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"); 

Comments

0

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'; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.