[HN Gopher] F-strings for C++26 proposal [pdf]
       ___________________________________________________________________
        
       F-strings for C++26 proposal [pdf]
        
       Author : HeliumHydride
       Score  : 175 points
       Date   : 2025-02-02 22:19 UTC (1 days ago)
        
 (HTM) web link (www.open-std.org)
 (TXT) w3m dump (www.open-std.org)
        
       | puffybuf wrote:
       | I'm pretty sure boost::format can do this, though not inline in
       | the string. Do we really need more complexity in cpp? isn't it
       | complex enough?
        
         | mkoubaa wrote:
         | This is the sort of change that adds complexity to the language
         | but reduces complexity in the code written in the language. We
         | take those
        
           | puffybuf wrote:
           | how would this work with internationalized strings?
           | especially if you have to change the order of things? You'd
           | still need a string version with object ordering I would
           | think
        
             | mkoubaa wrote:
             | I'm skeptical that people would want to do this in a single
             | expression.
        
               | rerdavies wrote:
               | Do what? Allow translators to reorder the appearance of
               | arguments in a translated format string? It's a
               | completely routine (and completely necessary) feature
               | when doing translations.
        
             | edflsafoiewq wrote:
             | f-strings are not an internationalization library.
        
               | wakawaka28 wrote:
               | The question was, how would you use this if you have i18n
               | requirements. Format strings are normally part of a
               | translation. I think the bad answer is to embed the
               | entire f-string for a translation as usual, except this
               | can't work because C++ f-strings would need to be
               | compiled. The better answer is, don't use f-strings for
               | this because you don't want translators to monkey around
               | with code and you don't want to compile 50 versions of
               | your code.
        
               | mkoubaa wrote:
               | C++ is such a narrow skillset that I'd rather not roll
               | the dice on translators knowing what to do
        
               | wakawaka28 wrote:
               | Even if you told them, "just copy the names from the
               | original string" it's still asking for trouble, and maybe
               | even security holes if they don't follow instructions.
               | But the biggest problem with the idea is surely that the
               | strings need to be compiled.
        
           | hackyhacky wrote:
           | > This is the sort of change that adds complexity to the
           | language but reduces complexity in the code written in the
           | language. We take those
           | 
           | An admirable statement of policy, but I'm not sure it's
           | possible. Adding complexity to the language means there are
           | more gotchas and edge-cases that a programmer must consider,
           | even if they don't use the feature in question.
        
             | verall wrote:
             | > Adding complexity to the language means there are more
             | gotchas and edge-cases that a programmer must consider,
             | even if they don't use the feature in question.
             | 
             | Since this is C++, this is not a problem we have to
             | consider
        
               | pjmlp wrote:
               | This is a meme by now, yet it isn't as if Python 3.13 is
               | a simple as Python 1.0, Java 23 versus Java 1.0, .NET 9
               | with C# 13 versus .NET 1.0 with C# 1.0 and a Framework
               | reboot,....
        
               | cylemons wrote:
               | C# has a lot of features but most of them feel like
               | simple syntactic sugar that make the language a joy to
               | use and they interact nicely together.
               | 
               | C++ has lots of features that interact with each other in
               | unexpected ways that could leak memory or access freed
               | memory etc.
        
               | pjmlp wrote:
               | C# has already enough material for pub Quiz, and no, not
               | all of them are syntatic sugar, and require deep
               | knowledge of the .NET runtime, and the way it interacts
               | with the host platforms.
               | 
               | I imagine you never went too deep into unsafe, cross
               | language interop, lambda evolution since the delegate
               | days, events infrastructure, pluggable GC, RCW/CCW, JIT
               | monitoring, the new COM replacement, how the runtime and
               | language features differ across .NET Framework, Core,
               | .NET MicroFramework, UWP, AOT compilation, Mono, .NET
               | standard versus Portable Class Libraries, CLS friendly
               | libraries,...
               | 
               | On top of that, all the standard frameworks that are part
               | of a full .NET install on Visual Studio, expected that
               | most C# developers know to at least have some passing
               | knowledge on how to use them.
        
               | neonsunset wrote:
               | There is no need to just throw keywords around.
               | 
               | For other readers - more than half of these are
               | irrelevant.
               | 
               | Writing general purpose application code rarely involves
               | thinking about implications of most of these (save for
               | NAOT as of lately I suppose).
               | 
               | Writing systems C# involves additional learning curve,
               | but if you are already familiar with C++, it comes down
               | to understanding the correct mapping between features,
               | learning strengths and weaknesses of the compiler and the
               | GC and maybe doing a cursory disassembly check now and
               | then, if you care about it.
        
               | mkoubaa wrote:
               | The original comment was about the divergence of the
               | complexity of a language and the complexity of programs
               | implemented in the language. I think the comment you
               | replied to with all its keywords and jargon beautifully
               | illustrated the point
        
               | neonsunset wrote:
               | No one of sane mind accesses even one tenth of these on a
               | daily basis.
               | 
               | They simply do not matter. For example - CLS-
               | compatiblity, seriously? I'd return the favour and ask
               | the interviewer why they disagree with the .NET's team
               | stance that this lost relevance in early .NET versions
               | more than a decade ago.
               | 
               | There are main framework and features to be aware of,
               | there are some that may be relevant to legacy codebases
               | you must avoid like fire, and there are those to which
               | the only appropriate response would be "this never
               | existed, if it did, forget about it".
               | 
               | (to Pjmlp - please do not equate knowing the terms with
               | understanding them, and stop bringing up whatever was
               | left by wayside of history to people who should have no
               | business being bothered by this nonsense, thank you)
        
               | pjmlp wrote:
               | Just like not everyone accesses 100% of C++ on daily
               | basis on their work.
               | 
               | I do whatever I please, feel free to ignore my comments,
               | downvote them, or whatever goes on your heart regarding
               | them.
        
               | pjmlp wrote:
               | Many of those keywords as you call it, are part of a
               | technical interview in any .NET consulting shop worth
               | their business.
               | 
               | And while I expect any junior not to know half of them,
               | anyone claiming to be a senior better have an answer,
               | regardless of what I throw at them.
               | 
               | Naturally I don't expect anyone versed in desktop
               | frameworks to master backend and vice-versa, but they
               | better know the bits that relate to desktop in that case,
               | across the whole stack.
        
               | verall wrote:
               | Yes of course, and I mean it somewhat seriously - C++
               | engineers are used to the language being too complex for
               | anyone to completely understand. It's worth some more
               | incremental language complexity to support niceties like
               | fstrings without additional overhead.
        
             | truncate wrote:
             | Depends on case to case basis. I wouldn't generalize it to
             | every case. As a daily C++ engineer, I think overall many
             | features added over the years have mostly been positive.
             | There are features that I don't use and I don't think it
             | really affects much. That said, I do get the sentiment of
             | language becoming too syntactically complex.
             | 
             | I like this feature as string formatting is something
             | frequently used and this certainly looks cleaner and
             | quicker to write.
        
         | HeliumHydride wrote:
         | C++ also has std::format, which was introduced in C++20. This
         | is just sugar on top of it, except it also returns a container
         | type so that printing functions can have overloads that format
         | into a file or stream directly from an f-string, instead of
         | going through the overhead of a temporary string.
        
           | rerdavies wrote:
           | I'm wonder what this mysterious application is that is doing
           | heavy formatting of strings but can't afford the overhead of
           | a temporary string, and therefore requires horrifying and
           | inscrutable and dangerous language extensions.
        
             | chlorion wrote:
             | Being able to use string formatting without a heap is
             | pretty cool.
             | 
             | Rusts string formatting machinery does not require any heap
             | allocations at all, you can for example impl fmt::Write for
             | a struct that writes directly to a serial console byte-by-
             | byte with no allocations, and then you have access to all
             | of rusts string formatting features available to print over
             | a serial console!
             | 
             | I'm not sure about the horrifying and dangerous extensions
             | part though, I'm not really a C++ expert so I don't know if
             | there's a better way to do what they want to do.
        
             | badmintonbaseba wrote:
             | freestanding maybe? Embedded apps often don't shy away from
             | using something like printf, yet they don't like
             | unnecessary allocations.
        
         | nikhilsimha wrote:
         | just skimmed the proposal, dont see how inline rendered
         | f-strings are more complicated than the alternative.
        
         | richrichardsson wrote:
         | So it's less complex bringing in a 3rd party library and having
         | to pass arguments?
         | 
         | fmt library can also do something similar, but still requires
         | the complexity of adding the library and passing arguments.
        
           | hyperpl wrote:
           | especially bringing in boost which isn't allowed in some
           | codebases
        
             | krater23 wrote:
             | boot is not allowed caused by the complexity. So some
             | people disallow boost, here is the solution, just add the
             | complexity directly to the language definition!
        
       | mkoubaa wrote:
       | Tangent: this sort of thing can be implemented without any change
       | to libc++ (the runtime). Updates to compiler versions are
       | sometimes postponed by users with big codebases that treat a
       | libc++ change as something major.
       | 
       | Why don't we see gcc or clang or msvc back porting stuff like
       | this to an older version with a sort of future tag. It's normal
       | to see __future__ in the python ecosystem, for instance.
        
         | tart-lemonade wrote:
         | If a codebase is fragile enough that libc++ changes have to be
         | assumed breaking until proven otherwise, why take the risk?
         | Presumably the application already has a "standard" way of
         | formatting strings. If it ain't broke yada yada
        
           | mkoubaa wrote:
           | It's not about assumed breaking, it's that when you upgrade
           | libc++ you can become incompatible at runtime with your
           | distro or any other number of libraries outside your control
           | in ways that are difficult to detect
        
         | pjmlp wrote:
         | Because C++, just like C, Ada, Cobol, Fortran, Modula-2, Pascal
         | is an ISO driven language.
         | 
         | Whereas Python language evolution is driven by whatever CPython
         | reference implementation does.
         | 
         | Compilers are free to do whatever they want, but then that code
         | isn't portable.
        
           | mkoubaa wrote:
           | This is also true about
           | 
           | #pragma once
           | 
           | But it became a de facto standard at some point.
           | 
           | thank you for the clarification. You are 100% right about the
           | general difference. I didn't consider the level of
           | "confidence" python has in directing it's own evolution that
           | I don't detect in the C++ committee
        
           | pklausler wrote:
           | > Compilers are free to do whatever they want, but then that
           | code isn't portable.
           | 
           | At a practical level, this is no different from Fortran, "ISO
           | driven" or not.
        
       | edflsafoiewq wrote:
       | 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 wrote:
         | Yes. The basic idea is that there's a specifier that allows a
         | formatted string to transparently decay into an ordinary string
         | (a 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 wrote:
         | This 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 wrote:
         | Hah. 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 wrote:
           | It 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 wrote:
             | Yeah. 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 wrote:
         | > 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 wrote:
           | This 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 wrote:
             | Honestly can't tell if this is sarcasm. XD
        
             | nurettin wrote:
             | I 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 wrote:
               | Iteratively improving in previously released features
               | does not imply fixing issues caused by those features.
               | 
               | Constexpr and auto have nothing to do with macros.
        
               | nurettin wrote:
               | To 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.
        
               | gpderetta wrote:
               | > To me, redoing things that are not orthogonal implies
               | that the older version is being fixed
               | 
               | The older version is being improved, especially for
               | ergonomics. Regarding your examples, ranges do not
               | obsolete iterators, they are just a convenient way to
               | pass around iterator pairs, but actual range are better
               | implemented in terms of iterators when they are not just
               | a composition of ranges. Similarly move semantics has
               | little to do with nrvo (and in fact using move often is
               | suboptimal as it inhibits nrvo).
               | 
               | Again, I have no idea how constexpr and auto have
               | anything to do with macros.
        
             | kringlezz wrote:
             | Move semantics is only needed because C++ introduced
             | implicit copies (copy constructor) and they of course
             | fucked it up my making them non-destructive so they aren't
             | even 'zero cost'.
             | 
             | Constexpr and consteval are hacks that 1) should have just
             | been the default, and 2) shouldn't even be on the function
             | definition, it should instead have been a keyword on the
             | usage site: (and just use const)                 int f() {
             | ... } // any old regular function       const int x = f();
             | // this is always get evaluated at compile time, (or if it
             | can't, then fail to compile)       int y = f(); // this is
             | evaulated at runtime
             | 
             | That would be the sane way to do compile time functions.
        
               | serbuvlad wrote:
               | Eh not really accurate because C's const means immutable
               | not actually constant. So I get introducing constexpr to
               | actually mean constant. But, yeah, constexpr x = f()
               | should probably have worked as you described.
        
               | jlokier wrote:
               | const is different in C++ from const in C. const
               | variables in C++ are proper compile-time constants. In C
               | they are not (the nearest equivalents are #define and
               | enum values).
               | 
               | So in C++ "const x = EXPR" would make sense to request
               | compile-time evaluation, but in C it wouldn't.
        
               | OskarS wrote:
               | They absolutely are not. Look at this range for-loop:
               | for (const auto item: vec) { ... }
               | 
               | `item` is not a compile-time constant. It's different
               | every run of the loop.
        
               | blux wrote:
               | Could have been if backwards compatibility was not a
               | thing indeed.
               | 
               | Move constructors are not _needed_ , they don't solve a
               | 'problem', but improve on previous semantics.
        
               | OskarS wrote:
               | I agree that I would have preferred destructive moves,
               | but move semantics makes C++ a much richer and better
               | language. I kinda think pre-move semantics, C++ didn't
               | quite make "sense" as a systems programming language.
               | Move semantics really tied the room together.
               | const int x = f(); // this is always get evaluated at
               | compile time, (or if it can't, then fail to compile)
               | 
               | That's very silly. You're saying this should fail to
               | compile?                   void foo(int x) {
               | const int y = bar(x);         }
               | 
               | There's no way the compiler can run that, because it
               | doesn't know what x is (indeed, it would have a different
               | value every time you run the function with a new
               | argument). So your proposal would ditch const completely
               | except in the constexpr case, everything runtime would
               | have to be mutable.
               | 
               | So you respond "well, I didn't mean THAT kind of const,
               | you should have a different word for compile-time
               | constants and run-time non-mutability!" Congratulations,
               | you just invented constexpr.
               | 
               | There are many bad things about C++, but constexpr ain't
               | one of them.
        
               | kringlezz wrote:
               | >There's no way the compiler can run that, because it
               | doesn't know what x is (indeed, it would have a different
               | value every time you run the function with a new
               | argument). So your proposal would ditch const completely
               | except in the constexpr case, everything runtime would
               | have to be mutable.
               | 
               | Yeah, I see no problem with that. Non-constant
               | expressions usage of 'const' has always just seemed like
               | a waste of time for me, never found it useful. But I
               | guess a lot of people really liking typing const and
               | "preventing themselves from accidentally mutating a
               | variable" (when has that ever happened?), so as a
               | compromise I guess you can have a new keyword to force
               | constant expressions:                 constexpr auto x =
               | foo(); // always eval at compile time       const auto x
               | = foo(); // old timey const, probably runtime but maybe
               | got constant folded.
               | 
               | but it's not really a big deal what they keyword is, the
               | main point was that "give me a constant value" should be
               | at the usage site, not at the function definition.
        
               | vitus wrote:
               | > "preventing themselves from accidentally mutating a
               | variable" (when has that ever happened?)
               | 
               | I can't count the number of times I've seen someone new
               | to the language use map::operator[] without realizing
               | that it's a mutating operation.
        
               | amluto wrote:
               | It's extra delightful that vector::operator[] isn't
               | mutating, so you can change a vector to a map and get
               | rather different semantics.
        
               | mikepurvis wrote:
               | > when has that ever happened?
               | 
               | Maybe it's not a concern in C-family languages, but
               | rust's culture of defaulting to let and only using mut
               | when it's specifically required does feel very pleasant
               | and ergonomic when I'm in that headspace.
        
               | steveklabnik wrote:
               | > the main point was that "give me a constant value"
               | should be at the usage site, not at the function
               | definition.
               | 
               | The issue is, not everything can be done at compile time,
               | and so "I can use this at compile time" becomes part of
               | the signature because you want to ensure that it will
               | continue to be able to be used that way. Without it,
               | changes in your function could easily break your callers.
        
               | OskarS wrote:
               | Exactly right. There's a huge benefit to encode the
               | ability for compile-time evaluation in the signature of
               | the function itself. Much better than doing it "ad-hoc",
               | like how template instantiation does it. Sometimes it
               | will work, sometimes it doesn't. constexpr functions
               | always work.
        
               | UncleMeat wrote:
               | I like const because I can look at a function signature
               | and know that nothing downstream is mutating a parameter.
               | I can also write a function that returns a const
               | reference to a member and know that nobody in the future
               | will ever break my invariants.
               | 
               | This isn't about "oops, I didn't mean to mutate that."
               | This is about rapidly being able to reason about the
               | correctness of some code that is leveraging const-
               | qualified code.
        
               | jcelerier wrote:
               | > (when has that ever happened?)
               | 
               | to me, pretty much a few times per week at least
        
             | TheRealPomax wrote:
             | It might be getting tiresome because it keeps being true,
             | so people keep pointing it out.
        
             | groby_b wrote:
             | auto is fixing the problem of long-ass type names for
             | intermediaries thanks to templates and iterators.
             | 
             | Move is fixing the problem of unnecessary mass-construction
             | when you pass around containers.
             | 
             | std::ranges was introduced because dear fucking god the
             | syntax for iterating over a partial container. (And the
             | endless off-by-one errors)
             | 
             | concepts, among other things, fix (sorta) the utter shit
             | show that templates brought to error messages, as well as
             | debacles like SFINAE and std::enable_if.
             | 
             | You're right. They're not fixing problems created by
             | previous features. They're all fixing problems created or
             | made massively worse by templates.
        
         | nialv7 wrote:
         | One 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 wrote:
           | Smart 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 wrote:
             | And 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 wrote:
               | Most 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 wrote:
               | If 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.
        
               | shultays wrote:
               | What are the examples to std functions that uses a raw
               | pointer but does not borrow/expects pointer to be valid
               | past the function call?
        
               | tempodox wrote:
               | The fact that you have to ask that question makes GP's
               | point. There's no way to tell that from looking at a
               | function's interface at present.
        
               | shultays wrote:
               | Why? If all standard functions that take no
               | ownership/keep references are using raw pointers then it
               | behaves same as user code/C++ devs expect: if a function
               | is taking a pointer then it claims no ownership. You take
               | a look at standard_function(T*) and see raw pointer and
               | then can assume it is not taking ownership or keeping
               | references
        
           | jeroenhd wrote:
           | > lacks the basic safety features to avoid dangling pointers
           | 
           | It doesn't. Unfortunately, C++ programmers choose not to use
           | basic safety features for performance reasons (or aesthetics,
           | or disagreement with the idea that a language should take
           | into account that a programmer might make a mistake, but at
           | least performance is a good one), but C++ actually has quite
           | a few tricks to prevent the memory management issues that
           | cause C/C++ bugs.
           | 
           | Using modern C++ safety features won't completely prevent
           | bugs and memory issues, just like using Rust won't, but the
           | mess that causes the worst bugs is the result of a choice,
           | not the language itself.
        
             | aiono wrote:
             | > the mess that causes the worst bugs is the result of a
             | choice, not the language itself.
             | 
             | Even the creator of the language admitted that "just write
             | better code bro" approach doesn't work.
        
               | tsimionescu wrote:
               | Has he? He at least used to be the biggest proponent of
               | it, "just follow these standards and development
               | practices that I had to meticulously develop for the US
               | military, that no tool can automatically check, and
               | you'll be fine!".
        
               | aiono wrote:
               | Yes, it writes in the slide 6 of presentation that Bjarne
               | himself created: https://github.com/CppCon/CppCon2023/blo
               | b/main/Presentations...
        
             | tsimionescu wrote:
             | Tell that to the designers of the C++ standard library, and
             | the new features being added. They're the ones that keep
             | adding new features that depend on references and pointers
             | instead of std::shared_ptr or std::unique_ptr.
        
               | saagarjha wrote:
               | I think one problem here is that a lot of codebases have
               | their own smart pointers and unfortunately the only
               | currency type is the unsafe one :(
        
               | tsimionescu wrote:
               | I don't think this is the only reason. If it were, they
               | could easily have added overloads that work with both std
               | smart pointers and with plain pointers for compatibility.
               | Or they could add pointer type template parameters, maybe
               | with concepts for the right ownership semantics.
        
               | usefulcat wrote:
               | shared_ptr and unique_ptr aren't useful for reasoning
               | about the lifetimes of stack-based objects (unless you're
               | willing to require that such objects always be
               | dynamically allocated, which is often not a reasonable
               | requirement).
        
             | amluto wrote:
             | Do these "modern C++ safety features" actually exist in any
             | usable manner?
             | 
             | string_view, for example, is very modern and is utterly and
             | completely unsafe.
        
           | f1shy wrote:
           | I would not say stop using it. But just stick to the really
           | needed features, and stop adding more features every 3 years.
           | Nobody can keep up, not the developers, not the compilers...
           | is just insane.
        
           | pjmlp wrote:
           | First we need to rewrite the likes of LLVM, GCC, V8, CUDA,...
           | into something else.
           | 
           | Which is not going to happen in our lifetime, even the mighty
           | Rust depends on LLVM for its reference implementation.
        
             | lifthrasiir wrote:
             | Stop producing a new C++ code as much as possible, then. It
             | doesn't help to nitpick the weakest possible interpretation
             | without acknowledging as such.
        
               | pjmlp wrote:
               | Which by definition means no more improvements to LLVM
               | then, as one example, and by consequence no improvements
               | on Rust backend.
        
               | justinrubek wrote:
               | "as much as possible" nobody said anything about entirely
               | stopping.
        
       | amluto wrote:
       | This links to a "decays_to" proposal:
       | 
       | https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p33...
       | 
       | And observes that this additional feature is needed to avoid
       | dangling references. And, as a long time C++ programmer, this
       | illustrates one of the things I dislike most about C++. In most
       | languages, if you make a little mistake involving mixing up
       | something that references something else with something that
       | contains a copy, you end up with potential overhead or maybe
       | accidental mutation. In Rust, you get a compiler error. In C++,
       | you get use-after-free, and the code often even seems to work!
       | 
       | So now we expect people to type:                   auto s =
       | f"{foo}";
       | 
       | And those people expect s to act like a string. But the designers
       | (reasonably!) do not want f to unconditionally produce an actual
       | std::string for efficiency reasons, so there's a proposal to
       | allow f to produce a reference-like type (that's a class value,
       | not actually a reference), but for s to actually be std::string.
       | 
       | But, of course, more advanced users might know what they're doing
       | and want to bypass this hack, so:                   explicit auto
       | s = f"{foo}";
       | 
       | Does what they programmer actually typed: s captures foo by
       | reference.
       | 
       | What could possibly go wrong?
       | 
       | (Rust IMO gets this exactly right: shared xor mutable means plus
       | disallowing code that would be undefined behavior means that the
       | cases like this where the code might do the wrong thing _don't
       | compile_. Critically, none of this actually strictly requires
       | Rust's approach to memory management, although a GC'd version
       | might end up with (deterministic) runtime errors instead unless
       | some extra work is done to have stronger static checking. And I
       | think other languages should learn from this.)
        
         | edflsafoiewq wrote:
         | IOW I believe it's the same thing as Rust's format_args! macro,
         | but trying to get away without needing a separate format! macro
         | by using implicit conversions.
        
           | tialaramex wrote:
           | std::format_args! gets you a Arguments<'a> which we'll note
           | means it has an associated lifetime.
           | 
           | Today-I-learned, Arguments<'a> has a single useful function,
           | which appeared before I learned Rust but only very recently
           | became usable in compile time constants, as_str() ->
           | Option<&'static str>
           | 
           | format_args!("Boo!").as_str() is Some("Boo!")
           | 
           | If you format a literal, this always works, if you format
           | some non-literal the compiler _might_ realise the answer is a
           | compile time fixed string anyway and give you that string,
           | but it might not even if you think it should and no promises
           | are given.
        
             | orf wrote:
             | The most useful function is Arguments::fmt. "as_str" is
             | just a shortcut utility function.
        
               | lifthrasiir wrote:
               | It is not a shortcut because it can't be implemented
               | without knowing the `Arguments` internals.
               | `format_args!("{}", "boo").as_str()` returns None for
               | example.
        
               | orf wrote:
               | It's a shortcut in the sense that most, if not all
               | optimisations are shortcuts. This one allows you to
               | shortcut the usual formatting machinery if the result of
               | formatting is a static string.
               | 
               | Like all shortcuts, it's not something you can always
               | rely on.
        
               | lifthrasiir wrote:
               | It can be used to shortcut the formatting process, the
               | function itself is however not a shortcut in my opinion.
        
               | orf wrote:
               | Interesting viewpoint: I see this as a distinction
               | without a difference. I'm interested to know why you see
               | it differently? What is its use, if not as a shortcut?
        
               | tialaramex wrote:
               | But there is no Arguments::fmt ? Are you thinking of the
               | implementations of Debug::fmt and Display::fmt on
               | Arguments ? Trait implementation isn't the same kind of
               | thing at all.
        
               | orf wrote:
               | There is exactly one useful thing you can do with an
               | `Arguments` object: call `.fmt()` on it.
               | 
               | The whole reason for std::Arguments very _existence_ is
               | to call `std::Arguments::fmt` on it.
               | 
               | `.fmt()` is a trait implementation, but that doesn't
               | change anything (not sure what "kind of thing" refers to
               | here). It's still a function on std::Arguments.
        
               | tialaramex wrote:
               | I think you're quite muddled about what's going on here
               | 
               | The full name of this type is std::fmt::Arguments not
               | std::Arguments and even so there's no such thing as
               | std::fmt::Arguments::fmt - there is no function with that
               | name, we can only talk about this name (since it doesn't
               | exist) if we bring into context a specific trait such as
               | Display or Debug
               | 
               | So the full name of the thing you think is the "one
               | useful thing you can do with Arguments" is
               | 
               | <std::fmt::Arguments as std::fmt::Display>::fmt
               | 
               | or perhaps it's
               | 
               | <std::fmt::Arguments as std::fmt::Debug>::fmt
               | 
               | ... as I said, Arguments implements both traits, and
               | their sole function has the same name so we need to
               | disambiguate somehow if we mean one of these functions or
               | the other. For the function defined on Arguments itself,
               | as_str, it's already unambiguous.
               | 
               | In the end the Debug and Display traits are all just
               | ductwork, which is why as_str caught my attention.
        
         | thaumasiotes wrote:
         | > But, of course, more advanced users might know what they're
         | doing and want to bypass this hack, so:
         | explicit auto s = f"{foo}";
         | 
         | > Does what they programmer actually typed, so s captures foo
         | by reference.
         | 
         | Wouldn't this problem be best solved by... not declaring s to
         | have a guess-what-I-mean type? If you want to be explicit about
         | the type of s, why not just say what that type is? Wouldn't
         | that be _even more explicit_ than  "explicit auto"?
        
           | amluto wrote:
           | A general issue with C++ (and many statically typed languages
           | with generic) is hilariously long type names that may even be
           | implementation details. Using auto can be a huge time saver
           | and even necessary for some generic code. And people get in
           | the habit of using it.
        
       | dgfitz wrote:
       | Somehow I manage to get by just fine with c++11. I have
       | refactored more than a few codebases that use 17 or greater.
       | 
       | Strangely, the codebase became more maintainable afterwards.
        
         | ryandrake wrote:
         | Came here to post the same thing. C++11 was a major and
         | _practical_ step up from previous versions. I haven 't seen
         | anything in future standards that looked like it a tool I'd use
         | day-to-day building actual production software. Much of the
         | subsequent versions added things probably interesting to
         | compiler and language academics. "Default constructible and
         | assignable stateless lambdas?" Really?
        
           | vitus wrote:
           | Off the top of my head, C++17 brought slicker notation for
           | nested namespaces, digit separators for numeric literals (so
           | you can more easily read 1'000'000'000), improvements in type
           | deduction for pairs / tuples (so std::make_pair / make_tuple
           | are basically unnecessary now), guarantees in the standard
           | for copy elision / return value optimization in specific
           | circumstances,. Oh, and structured bindings (so you can now
           | write `for (const auto& [key, value] : map) { ... }`).
           | 
           | edit: I guess digit separators came in C++14, I'm always a
           | little fuzzy there since at work, we jumped straight from 11
           | -> 17.
           | 
           | C++20 brought a feature that C had decades prior: designated
           | initializers, except it's in a slightly crappier form. Also,
           | spaceship operator (three-way comparison).
           | 
           | Looking at cppreference, it looks like C++17 also brought if
           | constexpr, and standardized a bunch of nonstandard compiler
           | extensions like [[fallthrough]]. C++20 continued
           | standardizing more of those extensions, and also brought
           | concepts / constraints, which are a lot easier to use than
           | template metaprogramming.
           | 
           | You're at least somewhat right though -- none of these are
           | paradigm shifts as C++11 was compared to C++03 (especially
           | with the notion of ownership, especially in the context of
           | std::unique_ptr and std::move).
        
             | edflsafoiewq wrote:
             | 17 is worth it for std::filesystem alone. It also has
             | optional and variant.
        
               | fsloth wrote:
               | Filesystem is great. It's insane it took so long.
               | 
               | Optional is nice but slightly awkward in a non-garbage
               | collected language.
               | 
               | IMO variant is one of those things that should not exist
               | in standard.
               | 
               | It tries to implement discriminated union in C++ but that
               | feature is lame without true pattern matching. And you
               | can't implement pattern matching without thorough syntax
               | level support. So in my books it's in this academic
               | "let's pretend a while we are using some other
               | language..." category.
               | 
               | It's _occassionally_ convenient for sure.
        
               | pjmlp wrote:
               | Might still land on C++26, but most likely C++29, then
               | you'll have pattern matching.
        
               | OskarS wrote:
               | > It tries to implement discriminated union in C++ but
               | that feature is lame without true pattern matching. And
               | you can't implement pattern matching without thorough
               | syntax level support. So in my books it's in this
               | academic "let's pretend a while we are using some other
               | language..." category.
               | 
               | I agree, they should have made it a language/syntax
               | feature. However: if you wanna do a sum type, it does do
               | that. I'd rather have that than nothing.
        
             | darknavi wrote:
             | Designated initializers are so nice. Even though they have
             | C++-isms like required order, it adds safety and
             | readability to code.
        
               | rerdavies wrote:
               | The safety and readability are nice; but WHY do they have
               | to be in order? That is so typically clueless. Such an
               | obvious feature, screwed up in a way that only a C++
               | committee member could.
        
               | a_t48 wrote:
               | Initializers for members are _always_ run in the order
               | the member is declared - this applies even in constructor
               | initializing lists, see https://en.cppreference.com/w/cpp
               | /language/constructor#:~:te... - it doesn't matter what
               | order you declare the initializers in, and clang will
               | warn you if you write your initializers in an order other
               | than what they will be executed in. Designated
               | initializers are the same way, it's probably best that
               | the behavior is consistent across methods of
               | constructing.
               | 
               | Why is it this way? As best as I can find it's so that
               | destructors always run in reverse order of construction,
               | I guess there could be some edge cases there that matter.
               | It's not the strongest argument, but it's not nothing.
        
               | bregma wrote:
               | Why does construction and destruction order need to be
               | deterministic?
               | 
               | Well, consider what would happen if you had members whose
               | value depends on other members. For example, one member
               | is a pointer to another. Or perhaps one member uses RAII
               | to hold a lock and another controls a resource.
               | 
               | Deterministic construction and destruction order is a
               | fundamental feature of C++. Calling it clueless is just
               | an indication one does not know C++.
        
               | vitus wrote:
               | > Why does construction and destruction order need to be
               | deterministic?
               | 
               | That question presupposes a particular compiler
               | implementation of designated initializers. Indeed, C90
               | had the fixed-order requirement until C99 decided this
               | was unnecessary and removed it.
               | 
               | > Well, consider what would happen if you had members
               | whose value depends on other members.
               | 
               | Can you specify a designated initializer in that way,
               | though? Either you specify a value, or you don't; I'm not
               | aware of a way to introduce dependencies between members
               | with a designated initializer. Yes, you can add a default
               | initializer to a specific member, but that only kicks in
               | if it's unspecified by the designated initializer.
               | 
               | With a constructor initializer list, sure, you can
               | absolutely introduce dependencies on previously-
               | constructed members. But that's not the case with a
               | designated initializer.
        
           | FpUser wrote:
           | To me moving from C++11 to 17 and then 20 was just a matter
           | of convenience. When digging on how to do this and that I've
           | found few things that just saved my time here and there. Also
           | couple of valuable libs I wanted to use required newer C++
           | versions.
        
           | dataflow wrote:
           | constexpr in 11 vs 14 was night and day difference.
        
             | anonnon wrote:
             | constexpr "if" statements (C++20) are also a game-changer.
        
               | boguscoder wrote:
               | Those are c++17
        
               | pjmlp wrote:
               | Initially, C++20 and C++23 extended its use cases, and
               | combined with concepts is a pretty sweet spot for compile
               | time metaprogramming without SFINAE or tag dispatch
               | tricks.
               | 
               | Much better than having yet another syntax for macros.
        
           | gpderetta wrote:
           | > "Default constructible and assignable stateless lambdas?
           | 
           | this comes up surprisingly often.
        
         | qalmakka wrote:
         | C++20's concepts IMHO are a massive update over C++11. You can
         | basically remove almost 90% of inheritance with them without
         | incurring in any issue (you could do that earlier too, but at
         | the expense of incredibly hard to read error messages - now
         | that's basically solved thanks to concepts).
        
           | 3836293648 wrote:
           | C++ concepts are a failure due to them only checking one side
           | of the contract. And the other is basically impossible to
           | implement without breaking other parts of the language
        
             | gpderetta wrote:
             | concepts finally allowed retiring stupid SFINAE tricks.
             | They are a huge success if only for that.
        
           | derriz wrote:
           | I don't find the error messages produced by concepts much
           | better than old school template errors. Maybe I got used to
           | the latter with experience and definitely the compilers got
           | better at generating useful error messages for templates as
           | the years passed. On the other hand when I have to review
           | code where a significant portion of the source relates to
           | concepts, my heart sinks.
           | 
           | In my opinion, C++ "concepts" are the least useful C++20
           | addition to the language - awful syntax, redundancy
           | everywhere (multiple ways of writing the same thing). And for
           | what? Potentially better error messages?
           | 
           | Another gripe; of all the generic overloaded words available
           | to describe this C++ feature, "concept" must be the least
           | descriptive, least useful. Why pick such a meaningless name
           | that does absolutely nothing to even suggest what the feature
           | does?
        
             | TuxSH wrote:
             | > In my opinion, C++ "concepts" are the least useful C++20
             | addition to the language - awful syntax, redundancy
             | everywhere (multiple ways of writing the same thing).
             | 
             | They're not the least useful C++20 addition, in fact
             | they're amongst the most useful ones.
             | 
             | In particular the addition of the "requires" expression is
             | the real killer here.
             | 
             | > And for what? Potentially better error messages?
             | 
             | Removing even more enable_if and making template code even
             | easier to read (you could do some of that with if constexpr
             | + static_assert in C++17, but there were gotchas). Oh and
             | it allows you to check for the presence of members in
             | classes, which you couldn't do before.
        
       | efitz wrote:
       | When I saw the title I thought "F-strings" might be some novel
       | variant of P--strings. I was disappointed that this is just about
       | formatting. I really would prefer safer string handling in modern
       | C/++
        
       | andyg_blog wrote:
       | I agree that we should have safe-by-default "decay" behavior to a
       | plain ol std::string, but I'm also picking up that many aren't
       | certain it's a useful syntactic sugar in top of the fmt lib? Many
       | other languages have this same syntax and it quickly becomes your
       | go-to way to concatenate variables into a string. Even if it
       | didn't handle utf-8 out of the box, so what? The amount of
       | utility is still worth it.
        
       | mixmastamyk wrote:
       | So, the f-string in Python is "spelled" that way because another
       | leading character was the only ASCII syntax left for such a
       | thing. It's odd that PRQL and now potentially C++ might copy it.
       | In the PRQL case it was a new thing so they could have chosen
       | anything, double quotes (like shell interpolation) or even
       | backticks, that seem to make more sense.
       | 
       | Also the f- prefix was supposed to be short for format and
       | pronounced that way. But "eff" caught on and now devs the world
       | over are calling them "eff strings" ... funny. :-D
        
         | eviks wrote:
         | Why is it odd to copy a popular and fitting alternative? What's
         | the better one?
        
           | mixmastamyk wrote:
           | Because it's an variant with a wart, only picked from being
           | backed into a corner. I mentioned shell interpolation in my
           | previous comment.
        
         | snthpy wrote:
         | PRQL contributor here:
         | 
         | That is a valid point and something I've also been thinking
         | about lately. I can't speak for the others but in my case the
         | Python string interpolation syntax was the one I was most
         | familiar with, other than bash, so it was just the default. The
         | big idea really is to have string interpolation and the syntax
         | is somewhat secondary but we do aim for ergonomics with PRQL so
         | it is a consideration.
         | 
         | Since then I've seen more alternatives like `Hello ${var}!` in
         | JS/TS and $"Hello {var}!" in F#. Not sure that there's a clear
         | way to prefer one approach over the others.
         | 
         | What would you consider to be factors that would make you
         | prefer one over the others?
        
           | mixmastamyk wrote:
           | I suppose simplicity and familiarity would be the primary two
           | goals. Then congruence with SQL would be another factor
           | specifically for PRQL.
        
           | eviks wrote:
           | some factors:
           | 
           | ease of typing: so regular quotes are better vs backticks
           | (even with a prefix), F-prefix - better than $, requiring
           | Shift
           | 
           | ease of learning: here letter-mnemonic seems easiest: so
           | I-prefix for "interpolation" or E-prefix for "expression" or
           | maybe V-prefix for "variable". Or maybe F for "formatted" is
           | also fine?
           | 
           | familiarity: so F-prefix due to Python?
        
         | lifthrasiir wrote:
         | > [...] another leading character was the only ASCII syntax
         | left for such a thing.
         | 
         | Not really? The original PEP [1] for example considered
         | `i"asdf"` as an alternative syntax. Any ASCII Latin letter
         | besides from `b`, `r` and `u` would have been usable.
         | 
         | [1] https://peps.python.org/pep-0498/#how-to-denote-f-strings
        
           | mixmastamyk wrote:
           | As an alternative to another leading ascii character, you
           | offer another leading ascii character?
        
       | neonsunset wrote:
       | Reinventing C#'s FormattableString and interpolated string
       | handlers :)
        
         | pshirshov wrote:
         | C# wasn't the first language which introduced such mechanism.
        
           | debugnik wrote:
           | But they got the type decay right without introducing further
           | user-defined conversions, unlike this proposal. The syntax is
           | ad hoc, thus so should be the typing rule.
        
       | ggm wrote:
       | I'm going to make an asinine prediction. We will be exploring
       | F-strings in future languages in 100 years time, encountering the
       | same problems and questions.
       | 
       | I still use printf semantics in Python3 despite trying to get
       | with the program for symbolic string/template logic. I don't need
       | to be told it's better, I need some Philip-K-Dick level brain re-
       | wiring not to reach for                 "%d things I hate about
       | f-strings\n" % (int(many()))
       | 
       | modes of thinking.
        
         | bvrmn wrote:
         | Bless compilers able to catch wrong format specifiers.
        
         | orf wrote:
         | FYI that code sample is broken, it should be `(int(many()),)`
         | 
         | Ironic I guess?
        
           | lifthrasiir wrote:
           | It's not broken (try it!). Any value is interpreted as an
           | implicit 1-tuple if it's not a tuple nor a dict. A better
           | example would have been `"..." % many()` where `many` returns
           | a tuple or dict.
        
       | alterom wrote:
       | Yeah, I really missed ubiquitous C preprocessor macros in C++, so
       | let's bring them back, but now inside string literals. Sweet.
       | 
       | Seriously, I just keep being amazed that people are running with
       | the idea of having a full-blown untyped and unchecked _formatting
       | mini language_ (that 's what libfmt, which became C++20 format,
       | literally calls it) inside _string literals_ -- i.e., the part of
       | the source code that you 're specifically telling the compiler to
       | _not treat as code_.
       | 
       | Think about it for a minute.
        
         | HeliumHydride wrote:
         | Format strings in C++ are checked completely at compile time.
         | There are no hacks or compiler intrinsics involved (like what C
         | does for printf to verify format strings).
        
         | jandrewrogers wrote:
         | Eh? C++20 format is checked at compile-time. This has been
         | possible ever since string literals became constant
         | expressions. These features are within the standard compile-
         | time capabilities. People have done impressive compile-time
         | parsing and codegen using it.
        
         | qalmakka wrote:
         | So basically doing the same at runtime for the last 55 years
         | was somewhat OK?
        
       | feverzsj wrote:
       | Like the compile time isn't long enough. They should just let the
       | compiler do the job like Wformat=2 and skip any preprocess and
       | constexpr function.
        
         | pjmlp wrote:
         | It is fast enough when using precompiled headers, binary
         | libraries, and best of all C++20 modules with C++23 modularised
         | standard library.
         | 
         | Could it be better? Most likely.
        
           | forrestthewoods wrote:
           | C++ modules do not exist in practice and probably never will.
        
             | pjmlp wrote:
             | Office team apparently is of another opinion, as Vulkan
             | folks, and a few other early adopters.
             | 
             | You could just as well say that anything beyond C89 doesn't
             | exist, given its prevalence in some circles.
        
               | forrestthewoods wrote:
               | Yes. There are a very small handful of early adopters in
               | the year 2025 for a feature ostensibly added in C++20.
               | 
               | So, like I said, modules don't exist in practice and I'd
               | be shocked if in 2030 modules were considered normal.
               | 
               | C++11 was pretty game changing. C++14 and C++17 only took
               | a few years to reach widespread adoption.
               | 
               | It's very safe to require C++17 today. C++20 was a little
               | slower and because of the modules fuckup it's a bit
               | inconsistent. But it's largely fine to use.
               | 
               | C++23 probably needs another year or two. But also C++20
               | and beyond haven't added much that's worth upgrading for.
        
               | pjmlp wrote:
               | Like I said, it is a matter of point of view, and yes
               | such is the karma of ISO driven languages with multiple
               | implementations, when one cares about cross platform
               | code.
               | 
               | There are many folks that don't care though, for them it
               | is _" one platform, one compiler, language standard is
               | whatever my compiler allows me to do, including
               | extensions"_.
               | 
               | I am also quite bullish on the opinion that eventually,
               | C++26 might be the last standard, not that WG21 will stop
               | working on new ones, rather that is what many will care
               | about when using C++ in a polyglot environment, as it is
               | already the case in mobile OS platforms, the two major
               | desktop platforms and distributed computing (CNCF project
               | landscape).
               | 
               | Why C++26 and not earlier? Reflection.
        
               | rerdavies wrote:
               | > Reflection
               | 
               | Oh yes please! :-)
        
               | rerdavies wrote:
               | > C++20 and beyond haven't added much that's worth
               | upgrading for.
               | 
               | std::format is pretty nice (although not yet available on
               | Ubuntu 24.04 LTS.
               | 
               | Lambda capture of parameter packs is actually huge!
               | 
               | And ... I think it still remains to be see what the
               | outcome of modules will be.
               | 
               | One hopes (against hope) that the big payoff for modules
               | will be in tool-ability of C++. IDE support for languages
               | like C#, Java, typescript is vastly superior to C++ IDE
               | tooling. Perhaps. Maybe. Modules will provide a path that
               | will allow that to change. I don't think the benefits of
               | modules have yet fully played out.
        
               | pjmlp wrote:
               | Ironically C++ had such tooling in the past but got lost,
               | a bit like Roman technology as the Empire felt.
               | 
               | Visual Age for C++ v4.0 had a Smalltalk like experience
               | with a database storage for the code, and Lucid Energize
               | C++ already had something that people now know as LSP
               | (Cadillac on their implementation), with incremental
               | compilation and linking (at method/function level).
               | 
               | They failed commercially due to high prices and hardware
               | requirements.
               | 
               | We have had C++ Builder for decades for GUI RAD
               | development, Delphi/VB style, but due to how Borland went
               | after the enterprise and various changes of hands, very
               | few are aware that it exists and its capabilities.
               | 
               | C++ Builder with VCL was Java/.NET before these were even
               | an idea looking for an implementation.
               | 
               | Problem now is that C++ has become a specialized tooling
               | for high performance code, language runtimes, drivers and
               | GPGPU, so you write 90% of the code in
               | Java/C#/nodejs/..... and then reach out to native
               | libraries, for various reasons.
               | 
               | Still, Clion, Visual Studio, C++ Builder, are quite good
               | as far as development experience goes.
        
           | grandempire wrote:
           | > best of all C++20 modules
           | 
           | Do any compilers besides VS support this?
        
             | pjmlp wrote:
             | Clang with CMake and ninja as build system (cmake.js for
             | nodejs does as well).
             | 
             | The only missing piece is that cmake is still in the
             | process to support header units.
             | 
             | GCC is getting there.
        
             | 3836293648 wrote:
             | GCC supports it with a flag. C++23's import std is not
             | there at all though.
        
       | otteromkram wrote:
       | Decline based on usage of pascalCase in the first example. How
       | did that even happen?
        
         | 3836293648 wrote:
         | That isn't even PascalCase. This is camelCase.
        
       | Laiho wrote:
       | Is it a coincidence that all these quality life things start to
       | pop up after C++ is facing real competition for the first time?
       | Seems a bit odd to add print after using std::out for 30 years.
        
         | Capricorn2481 wrote:
         | What is this referring to? I would imagine whatever you
         | consider recent competition is actually not that recent.
        
           | vanviegen wrote:
           | On the time scale of c++, rust is very recent. :-)
        
             | tialaramex wrote:
             | Nerd alt-history story: What if Graydon decides he should
             | attend WG21 and so instead of Rust what we get is a decade
             | of attempts to fix C++ and reform the process, followed by
             | burn out?
        
               | hnlmorg wrote:
               | Then we'd be supporting a different language that shares
               | the same or similar ideals as Rust. Whether that's
               | something already in existence or something entirely new.
               | 
               | Rust isn't really that unique, there are plenty of other
               | safe languages out there. And if Graydon was alone in
               | wanting something like Rust then Rust wouldn't have grown
               | in popularity like it has.
               | 
               | Rust exists because enough people thought there was a
               | need for Rust to exist. So if that wasn't Graydon with
               | Rust, then it would have been someone else with something
               | else.
               | 
               | This isn't meant to take anything away from Graydon nor
               | Rust. Just saying that innovations seldom happen in
               | silos. They're usually a result of teams of people
               | lusting for change.
        
               | pjmlp wrote:
               | Rust was helped by being a Mozilla language, and some of
               | the personalities it had around it.
               | 
               | The big plus of the language was proving that Cyclone
               | ideas to improve C, from AT&T research project were sound
               | and could be made mainstream.
               | 
               | And now other languages are building on it as well, that
               | is why Swift, Chapel, Haskell, OCaml, D are also having a
               | go at a mix of linear types, affine types and effects.
               | 
               | However many folks credit Rust for type system features
               | that are actually available in any ML derived language,
               | or Ada/SPARK, so it isn't as if knowledge is that well
               | spread.
        
               | hnlmorg wrote:
               | > Rust was helped by being a Mozilla language, and some
               | of the personalities it had around it.
               | 
               | Indeed. But my point is there was already widespread
               | movement behind building a programming language. So if
               | Mozilla hadn't taken charge then I'm certain someone
               | will.
               | 
               | My point is that Rust was born from a wider desire for
               | change rather than that desire existing because of Rust.
               | Thus that desire would have been met in one form or
               | another regardless of the invention of Rust.
        
               | rat87 wrote:
               | I think rust was helped by being part of Mozilla and
               | really helped when they got experienced devs (guys who
               | made ruby Bundler) to build the Cargo package manager pre
               | 1.0
               | 
               | https://web.archive.org/web/20221206052719/https://mail.m
               | ozi...
               | 
               | And helped a bit when they took a lot of stuff out the
               | stdlib into packages for the new package manager.
               | 
               | And helped a lot with a heavy focus pretty early on great
               | compiler messages (inspired by elm) and with a focus on
               | tools and documentation more generally.
               | 
               | Like a lot of things in life, rust was in the right place
               | at the right time to get popular. I do think the deep
               | want for something better and safer then c++ helped but
               | they made a lot of good choices(not necessarily the best
               | choices but good enough choices) and had some money
               | backing them. I think it was far from inevitable that
               | some other language to compete with c++ would have come
               | out anytime soon if rust hadn't been around (and hadn't
               | made good enough choices). It might have happened but
               | decent chances it wouldn't have.
        
               | hnlmorg wrote:
               | > I think rust was helped by being part of Mozilla and
               | really helped when they got experienced devs (guys who
               | made ruby Bundler) to build the Cargo package manager pre
               | 1.0
               | 
               | That's one of the reasons Rust became as widespread as it
               | is now. However we are talking about a "what if Rust
               | never existed" scenario. I'm confident that kind of
               | scenario we'd be talking about a different-yet-similar
               | language, maybe one that never got invented in our
               | version of reality, as having the same or similar forces
               | that helped that hypothetical language.
               | 
               | My point is that people wanted a successor to C++. So it
               | was going to happen. In our reality it was Rust. But if
               | Rust wasn't created by Greydon then someone else would
               | have created something else to fill that void.
               | 
               | > I think it was far from inevitable that some other
               | language to compete with c++ would have come out anytime
               | soon if rust hadn't been around (and hadn't made good
               | enough choices). It might have happened but decent
               | chances it wouldn't have.
               | 
               | I very much disagree with this assumption. We have D,
               | ObjectiveC, C#, Zig, Go, OCaml and others born out of the
               | need to to iterate and improve on what came before it.
               | But nothing had really caught on in the domain of safety
               | + zero-cost abstractions principle. And particularly not
               | aimed at C++ devs. It's been a contentious point for
               | years -- a void people have been looking to fill. So it
               | was only a matter of time before something caught on.
               | 
               | But this is all hypothetical. Plus if you subscribe to
               | the many-worlds interpretation of quantum mechanics, then
               | arguably we're both right :D
        
       | biofox wrote:
       | Making any changes to the core language is a sensitive thing as
       | it inevitably imposes new demands on compilers, a learning curve
       | for all users of the language, and risks breaking compatibility
       | and introducing unforeseen issues that will need to be fixed with
       | future changes to the language.
       | 
       | Personally, I'd much prefer a smaller and more stable language.
        
         | eviks wrote:
         | Leaving curve can decrease as a result of better design, same
         | re. the chance of those unforeseen issues (and it can even
         | decrease the chance of existing bugs popping up)
        
       | boxed wrote:
       | Jesus. This is such a bad idea. Don't repeat the mistakes of
       | Python. Look at what Swift does and make a SANE system ffs.
        
         | hikarudo wrote:
         | Sincere question: what's wrong with Python f-strings?
        
           | boxed wrote:
           | Three people in a row and not one of you guys checked out
           | Swift strings before commenting, thus making exactly the
           | mistake I complained about.
           | 
           | Look up Swift strings. Python has 4 types of string literals.
           | Swift has 1. And they are BETTER and more powerful. Cleaner.
        
             | edflsafoiewq wrote:
             | Swift has "normal strings", #"raw strings"#, and """ for
             | multiline strings. For interpolation, it uses "The answer
             | is \\(1+2)", but in a raw string you need #"The answer is
             | \\#(1+2)"#.
             | 
             | So compared to Python, string interpolation is always "on"
             | and doesn't need an f-prefix. Because it uses the string
             | escaping syntax, it doesn't have to take over a regular
             | character like {, which requires {{ escaping.
        
               | Mond_ wrote:
               | Is this supposed to be better than python? "Every string
               | is an f-string, make sure you don't accidentally miss
               | some interpolation." sounds like a step down, not like an
               | improvement to me!
        
               | edflsafoiewq wrote:
               | What do you mean by "miss some interpolation"?
               | 
               | There are two errors you could make in Python.
               | Accidentally using {} in a normal string where you wanted
               | interpolation, and accidentally using {} in an f-string
               | where you wanted literal {}. I definitely do the former a
               | lot.
        
               | boxed wrote:
               | The escape character is \\. Same as in Python. But in
               | Python you have to watch out for \ AND {. In Swift, only
               | \\.
               | 
               | And you can do this:
               | 
               | ####"foo ###\\(this is not interpolation) ####\\(but this
               | is) bar"####
               | 
               | In Pythons f-strings you can't ever write the literal
               | `{foo}` in a string for documentation for example. It's a
               | mess.
        
               | boxed wrote:
               | Close. But wrong.
               | 
               | Swift doesn't have raw strings and normal strings. Swift
               | has ONE syntax:
               | 
               | {# _n} "string {#_n}\\(interpolation)"{#*n}
               | 
               | Where n can be zero or more. So let's recap:
               | 
               | 1. ONE rule for start/end/escape prefix: A number of #
               | that has to match. 2. ONE rule for escape: \\.
               | 
               | I do Python full time and I wouldn't consider switching
               | to Swift, but the string situation is horrible compared
               | to Swift.
        
               | edflsafoiewq wrote:
               | Yes it does, because normal strings aren't just a special
               | case of raw strings when the number of #s is 0. The
               | presence of #s change the string syntax. Otherwise you
               | could as well say Python has one syntax, with optional f
               | and r prefixes.
        
         | VagabundoP wrote:
         | Most people really like python f-strings.
         | 
         | You might be on your own here.
        
           | boxed wrote:
           | You missed half of what I said. Look at Swift.
           | 
           | To quote myself:
           | 
           | Yea, f-strings are nice. But f-strings, r-strings, \
           | escaping, {{ escaping, ''' strings? Horrible.
           | 
           | Swift strings have ONE string. Just a single clean design
           | that does all of that. With a simple set of rules.
        
             | Smar wrote:
             | I suppose Swift copied Ruby's strings, then.
        
               | boxed wrote:
               | Looking at Ruby strings it seems not. They have at least
               | two special syntaxes: escaping and interpolation. They
               | seem to have some weird "<<-" thing to, so that's another
               | special thing. That's at least three things so far. Oh,
               | and %q strings is a thing. Four things.
               | 
               | Swift has just 1 string.
        
         | mort96 wrote:
         | F-strings is one of my favorite features of Python to be
         | honest.
         | 
         | That doesn't automatically mean it's a good idea in C++,
         | knowing C++ there are gonna be a whole lot of gotchas which
         | aren't in Python, but it means that, at least in my opinion,
         | how F-strings worked in Python is an argument in favor of them
         | rather than against them.
        
           | boxed wrote:
           | Yea, f-strings are nice. But f-strings, r-strings, \
           | escaping, {{ escaping, ''' strings? Horrible.
           | 
           | Swift strings have ONE string. Just a single clean design
           | that does all of that. With a simple set of rules.
        
       | nickysielicki wrote:
       | Reflection is going to change things so much.
        
       | beeforpork wrote:
       | It would be nice to take care to allow the use of GNU gettext()
       | or any other convenient translation tool.
       | 
       | Recap: _("foo %s") macroexpands to gettext("foo %s"), then "foo
       | %s" is extracted to a lexicon of strings by an external tool,
       | which can be translated and compiled into .po files, which are
       | loaded at runtime so gettext() can use a translated string based
       | on $LC_MESSAGES. (And there is also _N(..) for correct plural
       | handling.)
       | 
       | To do this with f-strings, _(f"foo {name()}") (which is a bit
       | ugly...) needs to translate to make_formatted_string(_("foo {}"),
       | name()) -- note that the _(...) needs to be called before calling
       | make_formatted_string, to be able to return a translated string.
       | 
       | I would wish for a proposal for f-strings to consider translating
       | strings, because we live in a world with many languages. And
       | maybe cite gettext as a convenient method, and think about what
       | could be done. Or point to a better tool. Or state: 'in that
       | case, f-strings cannot be used'.
        
         | eqvinox wrote:
         | I guess _ could be a function that both takes and returns
         | basic_formatted_string? (I.e. not gettext()).
        
         | TheRealPomax wrote:
         | The C++ language itself shouldn't be tied to any one specific
         | application or third party tool, though. Just because they
         | exist doesn't mean you are forced to use them, this is one of
         | those cases where f strings don't make a lot of sense. Things
         | with localized labels or text ideally have an id that gets
         | looked up, so you _can 't_ do English-based string composition.
         | Every locale gets looked up, no "just pass through the key if
         | locale X", and lookup failures don't "still work", they result
         | in super obvious, user-reportable nonsense.
        
       | vitaut wrote:
       | This proposal is not targeting C++26 and there is a new revision
       | of it: https://www.open-
       | std.org/jtc1/sc22/wg21/docs/papers/2025/p34...
        
       | Yoric wrote:
       | That's funny. We introduced essentially this features in OCaml
       | Batteries Included ~20 years ago. This brings back memories :)
        
       | robertlagrant wrote:
       | I'm hoping the next version gets the walrus operator.
        
       ___________________________________________________________________
       (page generated 2025-02-03 23:02 UTC)