5 ms·
So the f-string literal produces a basic_formatted_string, which is basically a reified argument list for std::format, instead of a basic_string. This allows eg
by edflsafoiewq 2y ago
So the f-string literal produces a basic_formatted_string, which is basically a reified argument list for std::format, instead of a basic_string. This allows eg. println to be overloaded to operate on basic_formatted_string without allocating an intermediate string
std::println("Center is: {}", getCenter());
std::println(f"Center is: {getCenter()}"); // same thing, no basic_string allocated
In exchange we have the following problems
// f-strings have unexpected type when using auto or type deduction.
// basic_string is expected here, but we get basic_formatted_string.
// This is especially bad because basic_formatted_string can contain
// dangling references.
auto s = f"Center is: {getCenter()}";
// f-strings won't work in places where providing a string currently
// works by using implicit conversion. For example, filesystem methods
// take paths. Providing a string is okay, since it will be implicitly
// converted to a path, but an f-string would require two implicit
// conversions, first to a string, then to path.
std::filesystem::exists(f"file{n}.dat"); // error, no matching overload
There are two other proposals to fix these problems.
- HeliumHydride 2y agoYes. The basic idea is that there's a specifier that allows a formatted string to transparently decay into an ordinary string (à la array-to-pointer decay) so that "auto" doesn't produce dangling references, and so that chains of more than one implicit conversion can take place.
- SkiFire13 2y agoThis seems pretty similar to Rust's `format_args!` macro, which however avoids these issues by being much more verbose and thus something people are less likely to use like in those examples. It does however have issues due to the abundant use of temporaries, which makes it hard to use when not immediately passed to a function. I wonder if C++'s fstrings have the same issue.
- ajb 2y agoHah. What's interesting about this is that since it doesn't require everything to actually be converted to a string, one can implement things other than just printing. So you could also implement interpretation, eg: pylist = python(f"[ y*{coef} for y in {pylist} if y > {threshold}]")
- jeroenhd 2y agoIt also allow for things that will set off spidey senses in programmers everywhere despite theoretically being completely safe assuming mydb::sql() handles escaping in the format string: cursor = mydb::sql(f"UPDATE user SET password={password} WHERE user.id={userid}")
- ajb 2y agoYeah. You really want "mydb::sql" to not take a basic_string, only a basic_formatted_string, so it will not compile if the conversion actually happened somehow.
- serbuvlad 2y ago> There are two other proposals to fix these problems. Most new features of C++ are introduced to fix problems created by previously new features added to C++.
- blux 2y agoThis is becoming such a tiresome opinion. How are concepts fixing a problem created by previous features to the langue? What about ranges? Auto? Move semantics? Coroutines? Constexpr? Consteval? It is time for this narrative to stop.
- nialv7 2y agoHonestly can't tell if this is sarcasm. XD
- nurettin 2y agoI thought the whole point of ranges is to solve problems created by iterators, move semantics to take care of scenarios where nrvo doesn't apply, constexpr and auto because we were hacking around it with macros (if you can even call it that)?
- gpderetta 2y agoIteratively improving in previously released features does not imply fixing issues caused by those features. Constexpr and auto have nothing to do with macros.
- nurettin 2y agoTo me, redoing things that are not orthogonal implies that the older version is being fixed. Being fixed implies that it was incorrect. And to clarify, sure, auto types and constexpr are entirely new things we didn't have (auto changed meaning but yeah), but we were trying to "get something like that" using macros.
- 2y ago
- nialv7 2y agoOne of the two other proposals is user defined type decay, which lets you choose what type auto will be deduced as. i.e. "auto x = y", x might not have the type of y, instead it can be anything you choose… This is like implicit type conversion on steroids. And all this because C++ lacks the basic safety features to avoid dangling pointers. Stop using C++ already!
- thegrim33 2y agoSmart pointers were added to the language 14 years ago. You're free to use old C++ with raw pointers and manual memory management, risking dangling pointers, or use modern C++, which provides smart pointers to avoid those issues.
- tsimionescu 2y agoAnd yet most if not all of the standard library keeps using pointer or reference arguments, not the new smart pointers that would actually document the ownership semantics.
- a_t48 2y agoMost arguments to standard library calls don't need to take ownership over memory, using a raw pointer or (const) reference is correct. Generally - smart pointers to designate ownership, raw pointers to "borrow".
- tsimionescu 2y agoIf a function takes a raw pointer, you need to check the docs to know if it is taking ownership or not. There is no general rule that applies to the whole of std that functions taking raw pointers assume that they are borrowing the value. And even if you could assume that pointer parameters represent borrowing, they are definitely not guaranteed to represent scoped borrowing: the function could store them somewhere, and then you end up with other issues. So shared_ptr is the only solution if you care about safety to represent a borrowed pointer. And of that's too costly, but the std designers did care about safety, they could have introduced a std::borrowed_ptr<T> that is just a wrapper around T* but that is used uniformly in all std functions that borrow a pointer and guarantee not to store it.
- account42 2y agoC++ desperately needs a solution for wrapper types that should eagerly decay into the wrapped type unless you pass it somewhere that explicitly wants the wrapper type.
- gpderetta 2y ago"operator auto" has been proposed a few times. But nailing down the semantics has proven elusive.