[HN Gopher] C++26: A Placeholder with No Name
       ___________________________________________________________________
        
       C++26: A Placeholder with No Name
        
       Author : jandeboevrie
       Score  : 85 points
       Date   : 2025-01-08 17:42 UTC (3 days ago)
        
 (HTM) web link (www.sandordargo.com)
 (TXT) w3m dump (www.sandordargo.com)
        
       | SideburnsOfDoom wrote:
       | See c#:
       | 
       | "A discard communicates intent to the compiler and others that
       | read your code: You intended to ignore it.
       | 
       | You indicate that a variable is a discard by assigning it the
       | underscore (_) as its name."
       | 
       | https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...
       | 
       | 2020: http://dontcodetired.com/blog/post/Variables-We-Dont-Need-
       | No...
        
         | tempodox wrote:
         | Yep, Microsoft took only almost 50 years to do what Prolog
         | could do in 1971, and the C++ standards committee took ~5 years
         | longer.
        
       | agumonkey wrote:
       | Makes me wonder when '_' was first used as a token to denote
       | unused information. Prolog ? ML ?
        
         | nicoburns wrote:
         | I think this predates computers entirely.
        
           | SideburnsOfDoom wrote:
           | I think so, yes. English novels written in the 1800s would
           | reference "Mr A------ from ------shire". As if they were
           | redacting personally identifying information. (1)
           | 
           | Underscores and dashes are not that different - especially as
           | this is not just pre-computer but pre-typewriter.
           | 
           | And now, underscores are a logical choice when the dash is
           | already in use as a minus sign.
           | 
           | 1) https://literature.stackexchange.com/questions/1944/why-
           | are-...
           | 
           | https://forums.welltrainedmind.com/topic/141704-jane-
           | austens...
        
             | adrian_b wrote:
             | Underscore has been used in programming for the first time
             | by the language IBM PL/I, in 1964-12, where it replaced the
             | hyphen/minus character that was used by COBOL to make more
             | readable the long identifiers.
             | 
             | Replacing hyphen/minus with underscore has been done
             | precisely for removing the ambiguity with the minus
             | operator (In COBOL the ambiguity was less annoying, because
             | the arithmetic operations were usually written with words,
             | e.g. ADD, SUBTRACT, MULTIPLY and so on).
        
           | agumonkey wrote:
           | Good point, this is clearly something that could go back
           | centuries.. if not all the way back to clay tablets.
        
         | lieks wrote:
         | If we're counting only programming languages (and not fill-out
         | forms), Prolog had it in 1971, before ML. ASCII didn't include
         | _ until its 1963 draft, so it's probably somewhere in that
         | time.
         | 
         | I'll guess it's probably Prolog, but maybe Planner (Prolog's
         | predecessor) had it too.
        
           | fanf2 wrote:
           | ASCII 1963 was the first version of the standard and it
           | lacked _ https://dl.acm.org/doi/10.1145/366707.367524
           | 
           | The 1965 draft had _
           | https://dl.acm.org/doi/10.1145/363831.363839
           | 
           | The first standard edition with _ was 1968 https://www.rfc-
           | editor.org/info/rfc20
           | 
           | The 1977 version is also available https://nvlpubs.nist.gov/n
           | istpubs/Legacy/FIPS/fipspub1-2-197...
        
       | kookamamie wrote:
       | What a convoluted mess.
        
         | asah wrote:
         | My thought exactly. But that's C++, a bolted-on mess of crap
         | whose redeeming features are (a) roughly the same execution
         | performance as C and assembly, and (b) it's safer and higher
         | productivity than C or assembly.
         | 
         | At this point, it feels like a matter of time before Rust
         | replaces C/C++.
        
           | dgfitz wrote:
           | There are decades and decades of code written in cobol that
           | run the modern banking system.
           | 
           | Multiply that code base size by like, 78.3, and you're
           | possibly in the same galaxy as the all the c++ codebases out
           | there that will be maintained for the next 50 years.
           | 
           | Rust may eat the lunch of c++ moving forward, the language
           | will never go away.
        
             | larodi wrote:
             | Abso-fkn-lutely! Stable computing protocols of all sorts
             | don't go away overnight and that is something everyone in
             | IT should absolutely get used to. I expect C/C++ to live
             | long beyond 2050.
        
             | DowsingSpoon wrote:
             | >the language will never go away
             | 
             | Just like COBOL! Seriously, _just like COBOL_. The language
             | will fade in importance over time. C++ will be relegated to
             | increasingly narrow niches. It will eventually only be
             | found in "legacy" systems that no one wants to spend to
             | rewrite. No one young will bother at all to learn C++ at
             | all. Then, in a few decades, they'll be pulling folks like
             | you and me out of retirement to maintain old systems for
             | big bucks.
        
               | timewizard wrote:
               | I doubt this analysis. The base of computing where C++ is
               | used is exponentially larger than the base where COBOL
               | was used. In particular the compilers we currently use
               | are written in it.
        
           | akkad33 wrote:
           | C++ is safer than C? How?
        
             | JTyQZSnP3cQGa8B wrote:
             | You can write whole applications that compiles to the same
             | assembly code without using any kind of memory management
             | thanks to the destructors, smart pointers, and all the
             | objects of the STL.
        
               | flohofwoe wrote:
               | This just pushes the problem into the C++ stdlib, which
               | has plenty of memory-corruption issues too but just calls
               | it 'undefined behaviour' (see things like dangling
               | std::string_view, missing range checks in containers or
               | iterator invalidation). In C you avoid those issues by
               | reducing dynamic memory allocations to a minimum,
               | prefering value types over reference types and using
               | function APIs as 'safety boundaries' (instead of directly
               | poking around in random data). Different approach, but
               | not any less safe than C++.
        
               | vlovich123 wrote:
               | > In C you avoid those issues by reducing dynamic memory
               | allocations to a minimum, prefering value types over
               | reference types and using function APIs as 'safety
               | boundaries'
               | 
               | "In Rust that's just pushing the problem to the borrow
               | checker and codegen which has plenty of memory corruption
               | issues too but just calls it "bugs". In C++ you avoid
               | those issues by reducing dynamic memory allocations to a
               | minimum, and using checked APIs as 'safety boundaries'
               | instead of directly poking around random arrays.
               | Different approach but not any less safe than C++".
               | 
               | Both statements are pretty ridiculous. It's pretty clear
               | that moving up in terms of the safe abstractions that are
               | possible makes your code safer because the safety is
               | there when you reach for that kind of programming
               | paradigm & the programming paradigms you need are
               | typically a property of the problem domain rather than
               | the language (it's it's the intersection of "how is this
               | problem solved" and "what tools does the language give
               | you"). In C it gives you few tools and you either twist
               | into a pretzel trying to stick to that or you write our
               | own tools from scratch every time and make the same but
               | different mistakes over and over. No language is perfect
               | and all will guide you to different takes on the same
               | problem that better suit to its paradigm, but there are
               | intractable parts that will force you to do things (e.g.
               | heap allocation and more involved ownership semantics are
               | not uncommon). Moreover C very limited ability to manage
               | your own codebase to define API boundaries - the only
               | tool is opaque types with a defined set of functions
               | declared in 2 different files.
        
               | uecker wrote:
               | The opaque types in C are great though. And because no
               | info leaks via header, you have very fast compilation.
               | The y"ou write from scratch every time" is a weird
               | statement. I do not delete my own code after each project
               | and there exist plenty of libraries.
        
             | UebVar wrote:
             | Somewhat separating owning and non owning memory in the
             | type system goes a long way. Also a much better standard
             | library and a stricter typing discipline.
             | 
             | The fact that it's mostly backwards compatible means you
             | can reproduce almost all issues of c in c++ awell, but the
             | average case fares much better. Real world C++ does not
             | have double-frees, for example. (As real world C++ does not
             | have free() calls).
        
           | flohofwoe wrote:
           | Tbf, Rust is catching up fast to become the same mess
           | (especially in the stdlib).
           | 
           | > it's safer and higher productivity than C or assembly
           | 
           | Debatable - in C and even more so in assembly, the unsafety
           | is at least directly in your face, while in C++ it's cleverly
           | diguised under C++ stdlib interfaces (like dangling
           | std::string_views, all the iterator invalidation scenarios
           | etc... meaning that memory corruption bugs may be much harder
           | to investigate in C++ than in C (or even assembly) - which in
           | turn affects productivty (e.g. what's the point in writing
           | higher level buggy code faster when debugging takes much
           | longer).
           | 
           | > it feels like a matter of time before Rust replaces C/C++
           | 
           | It may replace C++, but C will most likely outlive at least
           | C++, and I wouldn't be surprised if Rust too - even if just
           | as C API wrappers to allow code written in different modern
           | languages to talk to each other ;)
        
             | kibwen wrote:
             | _> Tbf, Rust is catching up fast to become the same mess
             | (especially in the stdlib)._
             | 
             | I've never seen any users who avoid using Rust's stdlib on
             | principle. The closest thing is the use of the parking_lot
             | crate over the standard Mutex types, but that's getting
             | better over time, not worse, as the stdlib moves closer to
             | parking_lot in both implementation and API. Other than
             | that, there's only been one "wrong" API that needed to be
             | deprecated with a vengeance (mem::uninitialized, replaced
             | by mem::MaybeUninit). Especially compared to C++, the fact
             | that Rust doesn't have a frozen ABI means it's free to
             | improve the stdlib in ways that C++ can only dream of.
             | While I do wish that the Rust stdlib contained some more
             | things (e.g. a standard interface to system entropy, rather
             | than making me pull in the getrandom crate), for what it
             | provides the Rust stdlib is quite good.
        
               | vlovich123 wrote:
               | Ironically c++ as well refused to define an ABI. That's
               | not why the language is bad. It's the lack of editorial
               | tools within the language the standard body has so it has
               | no evolution path and thus ossifies under its own
               | inertia. It's amazing they still haven't done anything
               | about it.
        
               | accelbred wrote:
               | I avoid the Rust stdlib. The stdlib uses panics and
               | dynamic memory allocation, neither of which are great for
               | embedded usecases or writing software adhering to safety-
               | critical coding practices. no_std is common for embedded
               | targets, and IIRC the linux kernel couldn't use stdlib
               | because of panics.
        
             | vlovich123 wrote:
             | > Rust is catching up fast to become the same mess
             | (especially in the stdlib)
             | 
             | Care to provide examples?
             | 
             | I think they generally do a very good job at curating the
             | APIs to feel very consistent. And with editions, they can
             | fix almost any mistakes they make whereas c++ doesn't have
             | that. In fact, the situation in c++ is so bad that they
             | can't fix any number of issues in basic containers and
             | refuse to introduce updated fixes because "it's confusing".
             | The things they keep adding to the stdlib aren't even core
             | useful things that come out of the box. Like missing a
             | networking API in 2025. The reason is they have to get it
             | perfect because otherwise they can't fix issues and they
             | can't get it perfect because networking is a continuously
             | moving target. Indeed we managed to get a new tcp standard
             | before c++ even though it had even worse ossification
             | issues. Or not caring about the ecosystem enough to define
             | a single dependency management system because the vendors
             | can't get agreement or a usable module system. Or macros
             | being a hot mess in c++ some 20 years after it was already
             | known to really suck.
             | 
             | Now it's possible given enough time rust will acquire
             | inconsistent warts over time similar to c++, but I just
             | don't think it'll ever be as bad in the standard library
             | due to editions and automated migration tools being easier
             | against rust. Similarly, I think editions give them the
             | flexibility to address even many language level issues.
             | Inertia behind ways of doing things are the harder
             | challenges that are shared between languages (eg adoption
             | of Unsend might prevent the simpler Move trait from being
             | explored and migrated to), but c++ is getting buried under
             | its own weight because it's not a maintainable language in
             | terms of the standard because the stewards refuse to
             | realize they don't have the necessary editorial tools and
             | keep not building themselves such tools (even namespace
             | versions ended up being aborted and largely unused).
        
           | poincaredisk wrote:
           | Rust is a C++ replacement, but not a C replacement, for many
           | C use cases. Language that may replace C is Zig.
        
             | pwdisswordfishz wrote:
             | What are those use cases?
        
           | readyplayernull wrote:
           | I find the C vs C++ battle amusing after having lived in the
           | 90s and the Pascal/Delphi vs C/C++ holy wars.
        
           | justin66 wrote:
           | > At this point, it feels like a matter of time before Rust
           | replaces C/C++.
           | 
           | I expect Rust's successor might have a shot at replacing C++.
           | 
           | By the time C++ was as old as Rust, it had conquered the
           | world. If Rust coulda, it woulda.
        
         | jayd16 wrote:
         | Seems pretty clean to me? Do you mean the current state of
         | affairs is a mess?
        
         | devnullbrain wrote:
         | I don't think the one character solution introduced at the
         | bottom of TFA is convoluted. Could you explain why you think
         | the solution - introduced at the bottom of TFA - is convoluted?
        
       | wild_pointer wrote:
       | "In addition, there are some variables such as locks and
       | scope_guards that are only used for their side effects"
       | 
       | ...
       | 
       | "This solution is also similar to other languages' features or
       | conventions"
       | 
       | As far as I know, in Rust you can't use "_" for that, as the
       | value will be dropped right away, so the mutex/resource/etc.
       | won't live for the scope.
        
         | awestroke wrote:
         | No, it lives until the end of its last scope regardless of name
        
           | tialaramex wrote:
           | This is not a name, it's the specific choice _not_ to assign
           | it to any name, and so your parent was correct that it 's
           | dropped immediately.
           | 
           | https://rust.godbolt.org/z/P1z7YeP4Y
           | 
           | As you see, Rust specifically rejects this code because it's
           | never what you meant, either write explicitly "I want to take
           | and then immediately give away the lock" via
           | drop(LOCK.lock()) (this really might be what you wanted, the
           | Linux kernel does this in a few places) or write an actual
           | named placeholder variable like in my third example function.
        
             | SideburnsOfDoom wrote:
             | > This is not a name, it's the specific choice not to
             | assign it to any name
             | 
             | Yeah, it's the same in c#. This is noticeable when in the
             | same scope you can have multlple "_" vars. If these were
             | actual names, they would be a name clash.
             | 
             | One of the uses is take some parts of a tuple return and
             | ignore the others.
             | 
             | e.g.
             | 
             | var (thingIwant, _, _, _) =
             | FunctionThaReturnsATupleOfFourThings();
             | 
             | There are three "_" in that expression, and they are
             | _different_ vars with different types. They don 't really
             | have the same name, they don't have names at all.
        
             | vlovich123 wrote:
             | But on the other hand the motivating problems noted in C++
             | don't exist. It is 100% legal to completely rebind a
             | variable in the same scope, it never warns that `_`
             | variables are unused nor about unused `_` prefixed
             | variables. I think `_` being immediately dropped is maybe
             | one of those unfortunate decisions we'll look back in 20
             | years and regret.
        
               | tempodox wrote:
               | Ever heard of `-Wunused-variable`?
               | 
               | > rebind a variable in the same scope
               | 
               | But only re-assigning values of the same type. Otherwise:
               | int x = foo();       int x = bar();       error:
               | redefinition of 'x'
               | 
               | and,                 int x = foo();       long x = bar();
               | error: redefinition of 'x'
               | 
               | What are you talking about?
        
       | wiseowise wrote:
       | Not every "problem" needs a solution.
       | 
       | At this point only LLMs will be able to decipher every intricacy
       | of C++.
        
         | lionkor wrote:
         | No, they cannot, they are word prediction machines.
        
         | devnullbrain wrote:
         | I swear some of you read 'C++' in the submission title and
         | immediately assume it's tagging on inscrutable, additional ways
         | of doing things. This is making C++ code _less_ intricate.
        
         | tempodox wrote:
         | What a wise display of complete ignorance.
        
       | ulbu wrote:
       | https://www.london-fire.gov.uk/media/2045/london-frie-brigad...
       | C++
        
       | carom wrote:
       | I am so shocked at how many people use `auto` in C++. I can not
       | think of a worse thing to do to your code in terms of readability
       | and future maintainability. Maybe it is OK if you use an IDE to
       | identify types for you but I still hate it. I am trying to learn
       | a new library right now with light documentation which means
       | reading the code, and between typedefs, auto, and technical debt,
       | it is a tedious exercise to figure out something's type, to go
       | look up its function, to see what type that is returning.
        
         | JTyQZSnP3cQGa8B wrote:
         | The auto keyword should not go in a public API, but internally
         | it's very useful especially when you create objects like `auto
         | ob = make_unique<VeryLongClassName>(...);` or any other kind of
         | function call where the type is obvious and would be identical
         | on both sides of an assignment.
         | 
         | As for your particular issue, using an IDE is essential, and
         | the typedef keyword is almost obsolete, so I guess you stumbled
         | upon a strange project. I would be curious to know what it is
         | if it's open-source.
        
           | carom wrote:
           | It is Slang. A very cool project and it only got its public
           | release relatively recently, so some sins are forgiven but
           | there are so many typedefs.                   typedef struct
           | ShaderReflection ProgramLayout;         typedef enum
           | SlangReflectionGenericArgType GenericArgType;
           | 
           | https://github.com/shader-
           | slang/slang/blob/master/include/sl...
        
             | JTyQZSnP3cQGa8B wrote:
             | It looks like C++98 to me: no `pragma once`, `typedef
             | uint32_t SlangUInt32` seems strange, `typedef bool
             | SlangBool` is definitely useless. The auto keyword is the
             | least of my problems here.
             | 
             | > years of collaboration between researchers at NVIDIA,
             | Carnegie Mellon University, Stanford, MIT, UCSD and the
             | University of Washington
             | 
             | Now I understand why, it's the kind of project that you
             | can't upgrade easily.
        
               | flohofwoe wrote:
               | `#pragma once` is not a panacea, it can still lead to
               | double inclusion in scenarios where the same include file
               | is accessible under different path names (granted, that's
               | a very esoteric scenario). Besides, `#pragma once` is
               | neither part of the C nor C++ standard. It's just a
               | common convention between compiler vendors - so
               | technically any code that uses pragme once as the only
               | include guard is not standard C or standard C++ (but tbf,
               | hardly any real-world code is fully standard compliant).
               | 
               | Typedef'ing common types to your own type names is
               | absolutely fine as long as it is unlikely to collide with
               | the typedefs of other libraries in the same project.
        
               | vitus wrote:
               | > Typedef'ing common types to your own type names is
               | absolutely fine as long as it is unlikely to collide with
               | the typedefs of other libraries in the same project.
               | 
               | I read the parent post as indicating that "this is C++;
               | we spell this as `using Foo = Bar;` now." Type aliases
               | (or namespace aliases, or using-declarations) are not
               | dead, but the typedef keyword in C++ is largely only
               | retained for backwards compatibility.
               | 
               | The core issue here is that type aliases add a layer of
               | indirection. That can be useful if the user shouldn't
               | need to know the implementation details and can interact
               | with said type purely through the API -- do I care if a
               | returned Handle is a pointer, an int, or some weird
               | custom class? Everyone is used to file descriptors being
               | ints, but you aren't going to do math on them.
        
               | elteto wrote:
               | #pragma once is a de-facto standard now, and if it's not
               | in the actual standard then that says more about the
               | failings of the standard writers than anything else.
               | 
               | And there's absolutely no reason to typedef _standard_
               | int types anymore. Not in C, and definitely not in C++.
               | That's just crusty old practices. Maybe if you want to
               | have nice short types like u8, i8, etc, I can understand
               | that. But SlangUint32 is just ugly.
        
               | bluGill wrote:
               | Pragma once isn't in the standard because there are cases
               | where it doesn't work and to standardize it means making
               | the standard significantly longer to catorgize when the
               | compiler is allowed to not work. I've been in discussions
               | and they all conclude it is not worth the effort
        
               | maccard wrote:
               | Pragma once is widely enough accepted that anyone who
               | argues against its use for that purpose better either be
               | able to tell me the compilers it doesn't work on, or I'm
               | going to assume they're being pedantic for the hell of
               | it.
               | 
               | And honestly, anyone who relies on the scenarios Pragma
               | once fails in (Compiling off network shared and symlinks)
               | should really fix those monstrous issues instead. The
               | places Pragma once trips up in are likely to be issues
               | for detecting modifications in incremental builds anyway.
        
               | throw16180339 wrote:
               | There used to be a couple holdouts, but all major C++
               | compilers have supported it for years
               | (https://en.wikipedia.org/wiki/Pragma_once).
        
               | bluGill wrote:
               | Prarma once works in the vast majority of cases. However
               | there are some rare ones where it fails. Every attempt to
               | fix those last ones breaks some other rare case or
               | profilings shows significant compile time increases.
               | 
               | use pragma once where it works in internal code but never
               | in headers you ship to someone else is a simple rule that
               | should work well enough.
        
               | maccard wrote:
               | > However there are some rare ones where it fails.
               | 
               | As I said in my previous comment, those cases are very
               | very often cases where the compialtion model is broken,
               | and it's held together by luck.
               | 
               | > Every attempt to fix those last ones breaks some other
               | rare case
               | 
               | My experience (and I have experiences of this) have been
               | that the cases where pragma once fails, other tools
               | (source control, built tools) cause "random" problems
               | that usually are hand waved away with "oh that problem,
               | just run this other makefile target that someone has put
               | together to clear that file and rebuild".
               | 
               | > or profilings shows significant compile time increases.
               | 
               | Again, my experience here has been that the compile time
               | change is usually a result of a broken project model, and
               | that there are other gotchas like "oh you can only
               | include files a.h and c.h but if you include b.h then
               | you'll break the internal build tool". Also, that taking
               | the hit to make your build correct unlocks optimisations
               | that weren't possible before.
               | 
               | Also, the projects that have these kinds of warts are
               | usually allergic to doing anything new whatsoever, making
               | any changes or improvements, upgrading compilers and
               | libraries. I suspect using C++17 is enough to scare them
               | off in many cases.
               | 
               | If it's good enough for QT or LLVM, it's good enough for
               | me.
        
               | ogoffart wrote:
               | I've been using #pragma once in my C++ libraries for a
               | decade and nobody ever reported any problem with that. I
               | did get bug report about using perfectly standard C++
               | features that were not properly implemented in some
               | compilers.
               | 
               | > the same include file is accessible under different
               | path names (granted, that's a very esoteric scenario)
               | 
               | And most likely a build system problem anyway if, say,
               | different versions of the same library get included in
               | your build.
        
               | bluGill wrote:
               | I use it and ran into issuses when I put a file into two
               | different locations using my package manager. The reason
               | I wanted a file in two different locations is something I
               | don't want to talk about.
        
           | cjfd wrote:
           | I agree that the auto keyword should be used sparingly.
           | Things you mention like the output of make_unique and
           | make_shared are a good exception since it is very clear what
           | the resulting type is. Also, one might use auto to store a
           | lambda in a local variable because it does not have a type
           | that you can type.
        
         | Too wrote:
         | Every other language does that by default with "var", "let", or
         | in some languages nothing at all. Within functions it doesn't
         | matter that much and using an IDE takes care of quick lookups
         | anyway.
        
           | dgfitz wrote:
           | Wasn't typescript created to fix this problem for JS?
        
             | jeremyjh wrote:
             | Using `let` does not make the expression un-typed in
             | Typescript. It means the type is inferred, and you'll get
             | type warnings if you use it where a different type is
             | expected.
        
           | maccard wrote:
           | Even for the non ide folks - vim emacs and vs code all have
           | excellent support for that.
        
             | ranger_danger wrote:
             | How does vim support this? I thought you had to use custom
             | scripts/extensions to do it?
        
               | maccard wrote:
               | Sorry I wasn't clear - all those editors have simple
               | (ish) plugins that support it
        
         | UebVar wrote:
         | If you don't use an IDE, you are doing it wrong, plain and
         | simple.
         | 
         | Editing png with a text editor is also much harder than editing
         | ppm. But there is no reason to consider this usecase when
         | defining a image format.
        
           | alexvitkov wrote:
           | You should never write code that's impossible to understand
           | without fancy IDE features. If you're writing such code, the
           | best thing you can do for yourself long term is switch to a
           | text editor without LSP (read Notepad) right now, which will
           | force you to start writing sane code.
           | 
           | This is true for any language, but it's especially true for
           | C++, where most large codebases have tons of invisible code
           | flying around - implicit casts, weird overloads, destructors,
           | all of these possibly virtual calls, possibly over type-
           | erased objects accessed accessed via smart pointers, possibly
           | over many threads - if you want to stand any chance of even
           | beginning to reason about all that you NEED to see the
           | actual, concrete, scientific types of things.
        
             | Jyaif wrote:
             | > You should never write code that's impossible to
             | understand without fancy IDE features
             | 
             | with Rust that ship has sailed
        
               | jeltz wrote:
               | I code Rust just fine without any fancy IDE you should
               | give it a shot. The languages I find hardest to code
               | without fancy IDE features are C and C++ due to their
               | implicit casts. Rust is typically easy to code without
               | IDE features due to its strong type system, lifetimes and
               | few implicit casts.
        
               | zbentley wrote:
               | Rust is one of my favorite new languages, but this is
               | just wrong.
               | 
               | > few implicit casts
               | 
               | Just because it doesn't (often) implicitly convert/pun
               | raw types doesn't mean it has "few implicit casts". Rust
               | has large amounts implicit conversion behavior (e.g.
               | deref coercion, implicit into), and semi-implicit
               | behavior (e.g. even regular explicit ".into()" distances
               | conversion behavior and the target type in code). The
               | affordances offered by these features are significant--I
               | like using them in many cases--but it's not exactly
               | turning over a new leaf re: explicitness.
               | 
               | Without good editor support for e.g. figuring out which
               | "into" implementation is being called by a "return
               | x.into()" statement, working in large and unfamiliar Rust
               | codebases can be just as much of a chore as rawdogging
               | C++ in no-plugins vim.
               | 
               | Like so many Rust features, it's not breaking with
               | specific semantics available in prior languages in its
               | niche (C++); rather, it's providing the same or similar
               | semantics in a much more consciously designed and user
               | focused way.
               | 
               | > lifetimes
               | 
               | How do lifetimes help (or interact with) IDE-less coding
               | friendliness? These seem orthogonal to me.
               | 
               | Lastly, I think Rust macros are the best pro-IDE argument
               | here. Compared to C/C++, the lower effort required (and
               | higher quality of tooling available) to quickly expand or
               | parse Rust macros means that IDE support for macro-heavy
               | code tends to be much better, and much better out of the
               | box without editor customization, in Rust. That's not an
               | endorsement of macro-everything-all-the-time, just an
               | observation re: IDE support.
        
               | jeltz wrote:
               | Have you actually tried coding Rust without IDE support?
               | I have. I code C and Rust professionally with basically
               | only syntax highlighting.
               | 
               | As for how lifetimes help? One of the more annoying parts
               | of coding C is to constantly have to look up who owns a
               | returned pointer. Should it be freed or not?
               | 
               | And I do not find into() to be an issue in practice.
        
             | adrian_b wrote:
             | While the C language has a lot of bad implicit casts that
             | should have never been allowed, mainly those involving
             | unsigned types, and which have been inherited by its
             | derivatives, implicit casts as a programming language
             | feature are extremely useful when used in the right way.
             | 
             | Implicit casts are the only reason for the existence of the
             | object-oriented programming languages, where any object can
             | be implicitly cast to any type from which it inherits, so
             | it can be passed as an argument to any function that
             | expects an argument of that type, including member
             | functions.
             | 
             | The whole purpose of inheritance is to allow the programmer
             | to use implicit casts. Otherwise, one would just declare a
             | structure member of the class from which one would inherit
             | in the OOP style and a virtual function table pointer, and
             | one could write an identical program with the OOP program,
             | but in a much more verbose way.
             | 
             | (In the C language, not only the implicit mixed signed-
             | unsigned casts are bad, but also any implicit unsigned-
             | unsigned casts are bad, because there are 2 interpretations
             | of "unsigned" frequently used in programs, as either non-
             | negative numbers or as modular numbers, and the direction
             | of the casts that do not lose information is reversed for
             | the 2 interpretations, i.e. for non-negative numbers it is
             | safe to cast only to a wider type, but for modular numbers
             | it is safe to cast only to a narrower type. Moreover, there
             | are also other interpretations of "unsigned", i.e. as
             | binary polynomials or as binary polynomial residues, which
             | cannot be inter-converted with numbers. For all these 4
             | interpretations, there are distinct machine instructions in
             | the instruction sets of popular CPUs, e.g. in the x86-64
             | and Aarch64 ISAs, which may be used in C programs through
             | compiler intrinsics. Even worse is that the latest C
             | standards specify that the overflow behavior of "unsigned"
             | is that of modular numbers, while the implicit casts of
             | "unsigned" are those of non-negative numbers. This
             | inconsistency guarantees the existence of perfectly legal C
             | programs, without any undefined behavior, but which
             | nonetheless compute incorrect "unsigned" values, regardless
             | which interpretation was intended for "unsigned".)
        
               | AlotOfReading wrote:
               | "Non-negative" unsigneds can be validly cast to smaller
               | types. That's why saturating_cast() exists. There are
               | modular numbers where casting to a smaller value is
               | likewise unsafe at a logical level. Your LCRNG won't give
               | you the right period when downcast, even if the modulus
               | value is unchanged.
        
               | alexvitkov wrote:
               | > Otherwise, one would just declare a structure member of
               | the class from which one would inherit in the OOP style
               | and a virtual function table pointer, and one could write
               | an identical program with the OOP program, but in a much
               | more verbose way.
               | 
               | No, you don't have to do that. Once you start thinking
               | about memory and manually managing it, it you'll figure
               | out there's simpler, better ways to structure your
               | program, rather than having a deep class hierarchy with a
               | gazillion heap-allocated objects, each with distinct
               | lifetime, all pointing at each other.
               | 
               | Here's a trivial example. Say you're writing a JSON
               | parser - if you approach it with an OOP mindset, you
               | would probably make a JSONValue class, maybe subclass it
               | with JSONNumber/String/Object/Array. You would walk over
               | the input string and heap allocate JSONValues as you go.
               | The problems with this are:                   1. Each
               | allocation can be very slow as it can enter the kernel
               | 2. Each allocation is a possible failure point, so the
               | number of failure points scales linearly with input size.
               | 3. When you free the structure, you must walk over the
               | entire tree and free each obejct one by one.         4.
               | The output of this function is suboptimal as the memory
               | allocator can return values that are far away in memory.
               | 
               | There's an alternate approach that solves all these
               | problems. If you're thinking about the lifetimes of your
               | data, you would notice that this entire data structure is
               | used and discarded at once, so you allocate a single big
               | buffer for all the nodes. You keep a pointer to the head
               | of that buffer, and when you need a new node, you stick
               | it in there and advance the pointer by its size. When
               | you're done you return the first node, which also happens
               | to be the start of the buffer.
               | 
               | Now you have a single point of failure - the buffer
               | allocation, your program is way faster, you only need to
               | free one thing when you're done, and your values are
               | tightly packed in memory, so whatever is using its output
               | will be faster as well. You've spent just a little time
               | thinking about memory and now you have a vastly superior
               | program in every single aspect, and you're happy.
        
               | knome wrote:
               | inheritance isn't required for object oriented
               | programming. the primary facet of oop is hiding
               | implementation details behind functions that manipulate
               | that data.
               | 
               | adding values to a dict via add() and removing them via
               | remove() should not expose to the caller if the
               | underlying implementation is an array of hash indexed
               | linked lists or what. the implementation can be changed
               | safely.
               | 
               | inheritance is orthogonal to object orientation. or
               | rather, inheritance requires oop, but oop does not
               | require inheritance.
               | 
               | golang lacks inheritance while remaining oop, for
               | instance, instead using interfaces that allows any type
               | implicitly defining the specified interface to be used
               | the.
        
               | adrian_b wrote:
               | "Hiding implementation details" means the same as "hiding
               | the actual data type of an object", which means the same
               | as "performing an implicit cast whenever the object is
               | passed as an argument to a function".
               | 
               | Using different words does not necessarily designate
               | different things. Most things that are promoted at a
               | certain time by fashions, like OOP, abuse terminology by
               | giving new names to old things in the attempt of
               | appearing more revolutionary than they really are.
               | 
               | Most classic works about OOP define OOP by the use of
               | inheritance and of virtual functions a.k.a. dynamic
               | polymorphism. Both features have been introduced by
               | SIMULA 67 and popularized by Smalltalk, the grandparents
               | of all OOP languages.
               | 
               | When these 2 features are removed, what remains from OOP
               | are the so-called abstract data types, like in CLU or
               | Alphard, where you have data types that are defined by
               | the list of functions that can process values of that
               | type, but without inheritance and with only static
               | polymorphism (a.k.a. overloading).
               | 
               | The example given by you for hiding an implementation is
               | not OOP, but it is just the plain use of modules, like in
               | the early versions of Ada, Mesa or Modula, which did not
               | have any OOP features, but they had modules, which can
               | export types or functions whose implementations are
               | hidden.
               | 
               | Because all 3 programming language concepts, modules,
               | abstract data types and OOP have as an important goal
               | preventing the access to implementation details, there is
               | some overlap between them, but they are nonetheless
               | distinct enough so that they should not be confused.
               | 
               | Modules are the most general mechanism for hiding
               | implementation details, so they should have been included
               | in any programming language, but the authors of most OOP
               | languages, especially in the past, have believed that the
               | hiding provided by granting access to private structure
               | a.k.a. class members only to member functions is good
               | enough for this purpose. However this leads sometimes to
               | awkward programs where some classes are defined only for
               | the purpose of hiding things, for which real modules
               | would have been more convenient, so many more recent
               | versions of OOP languages have added modules in some form
               | or another.
        
               | knome wrote:
               | I'll readily admit the languages were marketed that way,
               | but would argue inheritance was a functional, but poor,
               | imitation of dynamic message dispatch. Interfaces,
               | structural typing, or even simply swapping out object
               | types in a language with dynamic types does better for
               | enabling function-based message passing than inheritance
               | does, as they avoid the myriad pitfalls and limitations
               | associated with the technique.
               | 
               | Dynamic dispatch can be accomplished in any language with
               | a function type by using a structure full of functions to
               | dispatch the incoming invocations, as Linux does in C to
               | implement its file systems.
        
               | uecker wrote:
               | I am actually ok with the conversions and C and think
               | they are quite convenient. Unsigned in C is modular. I am
               | not sure what you mean by the "latest C standards
               | specify". This did not change. I also do not understand
               | what you mean by the "implicit cast of unsigned are those
               | of non-negative numbers". This seems wrong. If you
               | convert to a larger unsigned type, the value is unchanged
               | and if you convert to a smaller, it is reduced modulo.
        
               | adrian_b wrote:
               | In older C standards, the overflow of unsigned numbers
               | was undefined.
               | 
               | In recent C standards, it has been defined that unsigned
               | numbers behave with respect to the arithmetic operations
               | as modular numbers, which never overflow.
               | 
               | The implicit casts of C unsigned numbers are from
               | narrower to wider types, e.g. from "unsigned short" to
               | "unsigned" or from "unsigned" to "unsigned long".
               | 
               | These implicit casts are correct for non-negative
               | numbers, because all values that can be represented as
               | e.g. "unsigned short" are included among those
               | represented by "unsigned" and they are preserved by the
               | implicit casts.
               | 
               | However, these implicit casts are incorrect for modular
               | numbers, because they attempt to compute the inverse of a
               | non-invertible function.
               | 
               | For instance, if you have an "unsigned char" that is a
               | modular number with the value "3", it is incorrect to
               | convert it to an "unsigned short" modular number with the
               | value "3", because the same "unsigned char" "3"
               | corresponds also to 255 other "unsigned short" values,
               | i.e. to 259, 515, 781, 1027 and so on.
               | 
               | If you have some very weird reason when you want to
               | convert a number modulo 256 to a number modulo 65536 by
               | choosing a certain number among those with the same
               | residue modulo 256, then you must do this explicitly,
               | because it is not an information-preserving conversion.
               | 
               | If on the other hand you interpret a C "unsigned" as a
               | non-negative number, then the implicit casts are OK, but
               | you must add everywhere explicit checks for unsigned
               | overflow around the arithmetic operations, otherwise you
               | will obtain erroneous results.
        
               | uecker wrote:
               | The C89 standard has "A computation involving unsigned
               | operands can never overflou. because a result that cannot
               | be represented b! the resulting unsigned integer type is
               | reduced modulo the number that is one greater thnn the
               | largest value that can be represented by the resulting
               | unsipned integer type" (OCR errors) You can finde a copy
               | here: https://web.archive.org/web/20200909074736if_/https
               | ://www.pd...
               | 
               | Mathematically, there is no clearly defined way how one
               | would have to map from one residue system in modular
               | arithmetic to the next, so there is no "correct" or
               | "incorrect" way. Mapping to the smallest integer in the
               | equivalency class makes a lot of sense though, as it maps
               | corresponding integers to itself when going to a larger
               | type and and the reverse operation is then the inverse,
               | and this is exactly what C does.
        
           | foooorsyth wrote:
           | Not everyone reading your code will be using an IDE. People
           | may be passively searching your code on
           | GitHub/gerrit/codesearch.
           | 
           | val/var/let/auto declarations destroy the locality of
           | understanding of a variable declaration without an IDE + a
           | required jump-to-definition of a naive code reader. Also, a
           | corollary of this problem also exists: if you don't have an
           | explicit type hint in a variable declaration, even readers
           | that _are_ using an IDE have to do TWO jump-to-definition
           | actions to read the source of the variable type.
           | 
           | eg.
           | 
           | val foo = generateFoo()
           | 
           | Where generateFoo() has the signature fun generateFoo(): Foo
           | 
           | With the above code one would have to jump to definition on
           | generateFoo, then jump to definition on Foo to understand
           | what Foo is. In a language that requires the explicit type
           | hint at declaration, this is only one step.
           | 
           | There's a tradeoff here between pleasantries while writing
           | the code vs less immediate local understanding of future
           | readers / maintainers. It really bothers me when a ktlint
           | plugin actually fails a compilation because a code author
           | threw in an "unnecessary" type hint for clarity.
           | 
           | Related (but not directly addressing auto declarations):
           | "Greppability is an underrated code metric":
           | https://morizbuesing.com/blog/greppability-code-metric/
        
             | edflsafoiewq wrote:
             | If you accept f(g()), you've already accepted that the type
             | of every expression is not written down.
        
               | foooorsyth wrote:
               | I don't particularly accept f(g()). I like languages that
               | require argument labels (obj-c, swift). I would welcome a
               | language that required them for return values as well.
               | I'd even enjoy a compiler that injected omitted ones on
               | each build, so you can opt to type quickly while leaning
               | on the compiler for clarity beyond build time.
        
             | UebVar wrote:
             | The argument is tautological.
             | 
             | I want to use a text editor => This is the wrong tool =>
             | Yes, but I want to use a text editor.
             | 
             | These people do use the wrong tooling. The only way to cure
             | this grievance is to use proper tooling.
             | 
             | The github webui has some ide features, such as symbol
             | search. I don't see any reason why not use a proper ide.
             | github.dev is a simple click in the ui away. When you use
             | gerrit, do a local checkout, that's one git command.
             | 
             | If you refuse to use the correct tools for the job, your
             | experience is degraded. I don't see a reason to consider
             | this case when writing code.
        
               | foooorsyth wrote:
               | Have you ever worked in a large organization with many
               | environments? You may find yourself with a particular
               | interface that you don't know how to use. You search the
               | central code search tool for usages. Some other team IS
               | using the API, but in a completely different environment
               | and programming language, and they require special
               | hardware in their test loop, and they're located in
               | Shanghai. It will take you weeks to months to replicate
               | their setup. But your goal is to just understand how to
               | use your version of the same API. This is incredibly
               | common in big companies. If you're in a small org with
               | limited environments it's less of an issue.
        
               | UebVar wrote:
               | I have worked in big environments. My idea about "big"
               | might be naive, environments spanning different Oses and
               | different, including old languages like fortran and
               | pascal. But I never been in a situation where I couldn't
               | check out said code, and open it in my ide and build it.
               | If you can't that sounds like a another case of deficient
               | tooling. Justifying deficient tooling.
               | 
               | These where not some SWE wonderlands either. The code was
               | truly awful at times.
               | 
               | The Joel test is 25 years old. It's a industry standard.
               | I, and many other people consider it a minimum
               | requirement for software engineering. If code the "2. Can
               | you make a build in one step?" requirement i should be
               | ide-browsable in one step.
               | 
               | If it takes weeks to replicate a setup the whole
               | environment is deeply flawed. The one-step build is the
               | second point on the list because Joel considered it the
               | second most important thing, out of 12.
        
             | adrian_b wrote:
             | I do not agree that using an IDE matters.
             | 
             | If you cannot recognize the type of an expression that is
             | assigned to a variable, you do not understand the program
             | you are reading, so you must search its symbols anyway.
             | 
             | Writing redundantly the type when declaring the variable is
             | of no help when you do not know whether the left hand side
             | expression has the same type.
             | 
             | When reading any code base with which you are not familiar,
             | you must not use a bad text editor, but either a good text
             | editor designed for programmers or any other tool that
             | allows fast searching for the definitions of any symbols
             | encountered in the source text.
             | 
             | Adding useless redundancy to the source text only bloats
             | it, making reading more difficult, not easier.
             | 
             | I never use an IDE, but I always use good programming
             | language aware text editors.
        
             | chrisoverzero wrote:
             | > if you don't have an explicit type hint in a variable
             | declaration, even readers that are using an IDE have to do
             | TWO jump-to-definition actions to read the source of the
             | variable type.
             | 
             | This isn't necessarily the case. "Go to Definition" on the
             | `val` goes to the definition of the deduced type in the
             | IDEs and IDE-alikes I've ever used.
        
           | ranger_danger wrote:
           | Can we turn down the dogmatism please? I think you will find
           | that there are other equally valid perspectives if you look
           | around, and that the world is not so black and white.
        
         | readyplayernull wrote:
         | Isn't inline more "undeterministic" than auto? That is way
         | older and used everywhere.
         | 
         | I'd like auto functions.
        
           | jayd16 wrote:
           | Isn't it mostly meaningless outside of the syntax sugar of
           | putting code in the header?
        
         | FpUser wrote:
         | I am in a middle ground. Usually do not use auto but in the
         | cases like:                  for (auto member : set_of_members)
         | 
         | and some other that are similar by nature auto is a god
         | blessing.
        
           | secondcoming wrote:
           | Except this makes a copy of each member in set_of_members,
           | which is probably not what you want.
           | 
           | https://godbolt.org/z/1YnEs1M34
        
             | FpUser wrote:
             | This was to illustrate a point of auto rather than
             | intricacies of copying, referencing. I know what I want and
             | am familiar with auto&, const auto&, auto&& etc. etc.
        
         | zabzonk wrote:
         | Complicated template types, where you have a general idea of
         | the type but you don't want or need to spell it all out (it
         | might be very long) when the compiler can easily do it for you.
        
         | jcelerier wrote:
         | I have never seen anyone come back to typing types everywhere
         | after using auto for more than a couple months.
         | 
         | Use types when they are needed and use the tools at your
         | disposal (IDEs BT every text editor has clang language server
         | integration nowadays)
         | 
         | > with light documentation which means reading the code,
         | 
         | You have to read the code _anyways_ , documentation is
         | impossible to trust. There isn't one big library for which I
         | didn't have to go read the code at some point. Two weeks ago I
         | had to go read the internals of msvcrt, Microsoft's C runtime,
         | to understand undocumented features of process setup on
         | windows. I had to go read standard library code thousands of
         | times, and let's not talk about UI libraries.
        
           | coffeeaddict1 wrote:
           | > Use types when they are needed and use the tools at your
           | disposal (IDEs BT every text editor has clang language server
           | integration nowadays)
           | 
           | While I agree that auto is helpful, the amount of times I had
           | to wait for clangd (or whatever the IDE is using) to parse a
           | .cpp file and deduce the underlying type is frustrating. It
           | happens too often with every IDE (Qt Creator, CLion, VS
           | Studio, VS Code, etc...) I've tried whenever I'm programming
           | with a non-desktop machine that's not super beefy.
           | 
           | Plus I often use Github to search for code when I'm trying
           | out a new lib so having the type spelled out is extremely
           | helpful.
        
         | nly wrote:
         | It's not 1993. IDEs tell you the type if you hover over the
         | auto. Or control and click takes you to the type definition.
         | 
         | You have to weigh up the cost of going through the code and
         | changing all the type declarations                   auto x =
         | foo();
         | 
         | If you change the return type of foo here you don't have to
         | change 300 call sites. Personally i'd rather change it in one
         | place than 300.
        
           | AlotOfReading wrote:
           | Editor type deduction is surprisingly unreliable sometimes.
        
           | ninkendo wrote:
           | What about code reviewers? Show me a code review system that
           | lets you hover over the value to see the type... none of the
           | ones I've used can do it.
           | 
           | For that matter anyone reading the code from a web browser in
           | any other context.
        
             | dgfitz wrote:
             | Fwiw, I agree. I also pull down the branch under review in
             | parallel to the web code review. You'd (probably) not be
             | surprised by the number of times I've done this and the
             | code doesn't even build.
        
             | ranger_danger wrote:
             | I wonder if most of the reason people use auto is just to
             | save time when typing... if the IDE could auto-resolve the
             | type _in the source code_ when they use auto... would that
             | be a better compromise?
        
             | ogoffart wrote:
             | Even without auto you have the problem.
             | return foo().bar();
             | 
             | No `auto` and you still don't know the return type of foo.
             | And knowing the type might not be the only reason you'd
             | want an IDE anyway. What is `foo()` doing? I want to be
             | able to easily jump to the definition of that function to
             | verify that the assumption taken by the calling function
             | are correct.
        
             | ch33zer wrote:
             | This was probably rhetorical but metas code review tool
             | runs LSP and gives you clickable types where clicking takes
             | you to the definition.
        
         | Koshkin wrote:
         | 'auto' (in C++) and 'var' (in C# and Java) is a blessing, makes
         | code much less verbose. Also good for refactoring - less code
         | to change.
        
           | BalinKing wrote:
           | I'm only a C++ amateur, but IMHO C++ vs C#/Java isn't really
           | a fair comparison here--the latter doesn't have template
           | shenanigans and so types are much more transparent to the
           | reader (by which I mean that you don't have to execute a
           | dynamically-typed program in your head to get from the term
           | on the right-hand side to the type on the left).
        
           | bigstrat2003 wrote:
           | Verbosity is not bad. When it makes the code clearer, it is
           | even a good thing.
        
             | ithkuil wrote:
             | Complex type parameters make explicit typing highly
             | impractical to be used all the times
             | 
             | I like rust's approach in that it allows a mixture of
             | explicit types and type inference using placeholders
             | 
             | For example: "let x : Result<Foo<int, _>, _> = make_foo();"
        
         | dataflow wrote:
         | > I am so shocked at how many people use `auto` in C++.
         | 
         | I agree with you! But:
         | 
         | > I can not think of a worse thing to do to your code in terms
         | of readability and future maintainability.
         | 
         | Well, I definitely can. Using macros is one ;)
         | 
         | > I am trying to learn a new library right now with light
         | documentation which means reading the code, and between
         | typedefs, auto
         | 
         | I disagree with you on the typedefs. They're _much_ better than
         | auto. Auto doesn 't provide any type checking, it works
         | whatever the type is. Typedef tell you what the expected type
         | actually is.
        
         | benreesman wrote:
         | Herb Sutter has a pretty good explanation:
         | https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style...
        
         | lairv wrote:
         | Using auto in function parameters to have implicit templates is
         | very cursed
        
           | jayd16 wrote:
           | I'm pretty supportive of auto and var, etc. in languages but
           | parameters seem like a step too far.
        
         | cesaref wrote:
         | I can certainly relate to this experience. I remember when it
         | was introduced, I was very wary of getting too much 'auto' into
         | the codebase, certainly as the team could be a bit 'gung-ho'
         | adopting stuff just because it was there.
         | 
         | However, in hindsight, I think I was being overly conservative,
         | and it worked out well, and adoption didn't cause any obvious
         | problems.
         | 
         | Your concerns about learning a new library are valid, but the
         | problem is the library if it's not clear, or well documented.
         | To lay responsibility for this at the door of auto is a
         | stretch. You can write great and terrible code with a number of
         | language features (dubious use of goto is the classic example),
         | and it sounds like you are tackling a library which could do
         | with some love, either in documentation, or to clarify it's
         | conventions.
        
         | kgeist wrote:
         | My rule of thumb is to use auto only when the type is obvious
         | from the context. I think it's a sane compromise between
         | readability and non-verbosity.
        
         | MathMonkeyMan wrote:
         | I write my code with the assumption that the reader does _not_
         | have access to a "smart" IDE.
         | 
         | I use `auto` when the type is obvious or doesn't really matter,
         | and I seldom create aliases for types.
         | 
         | I feel like having verbose type names at function boundaries
         | and using `auto` for dependent types is the sweet spot. I'll
         | often avoid `auto` when referring to a class data member, so
         | the reader doesn't have to refer to the definition.
         | void foo(const std::multimap<double, Order>& sells) {
         | for (const auto& [price, order] : sells) {                 //
         | ...             }         }
         | 
         | but also                   void foo(const OrderBook& book) {
         | const std::multimap<double, Order>& sells = book.sells;
         | for (const auto& [price, order] : sells) {                 //
         | ...             }         }
         | 
         | `auto` is convenient for iterators. Which of the following is
         | better?                   auto iter = sells.begin();
         | std::multimap<double, Order>::const_iterator iter =
         | sells.begin();
        
           | Koshkin wrote:
           | Off-topic, but just wanted to note that using floating-point
           | numbers as keys may be generally a bad idea (unless you use a
           | custom comparator that takes into account the error that can
           | accumulate during calculations).
        
             | mgaunard wrote:
             | especially for an order book...
        
         | pjmlp wrote:
         | Programming without IDE is so 1970's...
         | 
         | Having said this, I usually only use type inference when the
         | types are obvious from context.
        
           | jayd16 wrote:
           | Was this tongue in cheek? If you _can_ use inference it was
           | at least obvious enough to the compiler. Otherwise you're
           | just saying "I use it when I feel like it."
        
             | pjmlp wrote:
             | auto x = func(); // no idea about func return type
             | auto x = new Widget(); // DRY             auto sum (auto a,
             | auto b); //  template function without boilerplate
             | 
             | Use the same principle in other contexts.
        
         | asveikau wrote:
         | There's so much redundancy built into the language if you
         | don't. Imagine:                   std::shared_ptr<T> p =
         | std::make_shared<T>();
         | 
         | Then replace T with a very long type. And that's not the most
         | verbose example, just an early one that popped into mind.
         | 
         | Then you have lambdas. Imagine assigning a lambda into a stack
         | variable without auto, also keeping in mind that std::function
         | adds overhead.
        
           | secondcoming wrote:
           | That example isn't what OP is talking about, because it's
           | obvious what the type of p is because it's on the same line:
           | auto p = std::make_shared<T>();
           | 
           | whereas the following isn't clear and isn't necessarily
           | correct without looking up what the return type of foo()
           | actually is:                   auto p = foo();
        
             | asveikau wrote:
             | I'll agree that your second example is less readable than
             | the first..
             | 
             | This could be mitigated with the name of foo() being more
             | descriptive.
             | 
             | If the return type is particularly wordy, auto could still
             | be appropriate.
        
               | unleaded wrote:
               | > This could be mitigated with the name of foo() being
               | more descriptive.
               | 
               | welcome back Hungarian notation
        
               | jeremyjh wrote:
               | auto conn = createConnection();
               | 
               | What does this have to do with hungarian notation?
        
               | pests wrote:
               | You're thinking of it wrong.
               | 
               | This works better:
               | 
               | auto uasStudents = getClassList()
               | 
               | In this case, "uas" prefix standing for an unsafe array
               | of strings.
               | 
               | Then say you validate the list
               | 
               | auto sasStudents = validate(uasStudents)
               | 
               | (Now it's a safe array of students!)
        
               | dullcrisp wrote:
               | I feel like I'm missing a joke.
        
               | powercf wrote:
               | Alone this looks like a reasonable use of auto. In a real
               | codebase, there may be two (or twenty) different
               | connection-like things, multiple of which may be
               | reasonably to call in this context.
               | 
               | The "Hungarian notation" comment is correct - it's not
               | strictly Hungarian notation, but annotating function (or
               | variable) names when the language has a type system
               | representing this same information is the same idea with
               | the same problems as Hungarian notation.
        
               | asveikau wrote:
               | lpwszThanks.
               | 
               | In seriousness, no, that's not what I'm suggesting, and I
               | find it an unusual thing to read from my comment. I'm
               | saying a descriptive name for foo() can give you a hint
               | about what the type is, even if it doesn't literally and
               | directly tell you what the type is.
        
         | maccard wrote:
         | Im as shocked as you are that people rely on textual
         | representations and ignore all the powerful tooling available
         | to them for understanding code.
         | 
         | Every editor I use has tools that will provide this information
         | in a single keystroke, macro or cluck. If you actively choose
         | to avoid using tools to read code, I shouldn't suffer for it.
        
           | mgaunard wrote:
           | if you need tools it just means the code is sub-par
        
             | maccard wrote:
             | Grep is a tool. But if I grep foo, it can't tell me the
             | difference between a function called foo, a variable called
             | foo, or any other types that may have a foo, or a comment
             | with foo in it. Even vscode can do "show me all uses of
             | foo" in a single click, and be perfectly correct,
        
               | mgaunard wrote:
               | It's not perfectly correct, and that actually what makes
               | it dangerous.
        
             | orf wrote:
             | That take is so comically nonsensical I question why you'd
             | even contribute it.
             | 
             | I have no doubt you read and write code without any tools.
        
         | ninkendo wrote:
         | There's a lot of arguments here where people are saying
         | basically, "auto is bad because you can ..." it "auto is great
         | because you can ...", as if the two are mutually exclusive or
         | something.
         | 
         | It's like saying "knives are bad because you can kill someone"
         | vs "knives are good because they can help make food"... nobody
         | thinks of knives as being an exclusively good or exclusively
         | bad thing; we all understand that context is key, and without
         | context it's meaningless.
         | 
         | Instead I feel it would be a lot more illuminating if the
         | discussion centered around rules of thumb... which contexts
         | auto is good, vs when it's bad. There's probably no complete
         | list, but a few heuristics would be great.
         | 
         | My 2C/:
         | 
         | Explicit type declaration is a form of documentation, used to
         | tell the _casual_ reader (ie. Often in a web browser, code
         | review, or someone seeing it copy /pasted as a snippet[0]) the
         | meaning of a piece of code. It's even better than comments:
         | it's guaranteed not to be a lie, or the code wouldn't compile.
         | 
         | I've seen this all the time working in Rust, Swift, typescript,
         | etc... sometimes an expression is super complicated, and the
         | type checker can infer the type just fine, and my IDE even
         | shows the type in an inlay... but I still worry that if these
         | weren't available, the expression would look super confusing to
         | a reader. So I make the type explicit to aid in overall
         | readability.
         | 
         | When to do this or not is a judgement call... different people
         | will come to different conclusions. But it's like any other
         | form of line-level documentation. Sometimes the code is self
         | explanatory, and sometimes it's not. But be kind to the casual
         | reader and use explicit types when it's very non-obvious what
         | the type would be.
         | 
         | [0] ie. Anyone without immediate access to an IDE or something
         | else that would show the type.
        
           | ranger_danger wrote:
           | > we all understand that context is key
           | 
           | Unfortunately I think this either this isn't actually the
           | case for many people, or too often they just never even stop
           | to consider that other perspectives might be possible, better
           | or even more common than their own.
           | 
           | In chatting with technical people online for the last 30
           | years, the biggest issue I have always had is their attitude.
           | IRC seems the worst for it but every platform has this
           | problem in my experience.
           | 
           | God complexes visible from space run rampant, people _always_
           | speaking in absolutes and seeing things as black and white,
           | complete lack of empathy and humility etc.
           | 
           | I think most arguments in the world, and even other things
           | like crime, might actually just stem from people's inability
           | to communicate with each other effectively.
        
         | nurettin wrote:
         | int wtf = omgtype(); // and read the compiler error
        
         | DonHopkins wrote:
         | Some kinky C++ programmers have such a sexual fetish for using
         | `auto` that they enjoy holding their breath as long as possible
         | while writing code, before ever declaring any explicit type
         | names. That's called auto-erotic asphyxiation!
        
         | ok123456 wrote:
         | Auto is preferred for assignment because it eliminates a whole
         | class of errors involving unintentional construction. Dropping
         | a const is the conanical example.
         | 
         | Auto in a function signature is syntactic sugar for introducing
         | a template parameter. It needs to be monomorphized at some
         | point to generate code.
        
         | nox101 wrote:
         | Types are not enough to solve this issue
         | 
         | https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...
        
         | fooker wrote:
         | Like everything in life, it has to be used in moderation.
         | 
         | auto it = data.begin();
         | 
         | Is a lot more readable than
         | 
         | std::vector<std::pair<std::vector<foo>, int>::iterator it =
         | ....
        
           | gigatexal wrote:
           | Oh boy! A vector of tuples of vectors and ints? Crazy
        
         | ch33zer wrote:
         | One area where auto is necessary is in coroutines. The types
         | are so hideous and abstract that writing them out is guaranteed
         | to be less readable than using auto and accepting the your
         | types are some blend of compiler derived and coroutine library
         | magic.
        
         | summerlight wrote:
         | My take is that `auto` is basically a tool to reduce local
         | redundancies rather than typing convenience. Rule of thumb: you
         | should avoid `auto` unless it actually improves readability
         | (e.g. significant reductions of syntactic redundancies), or
         | there is no other option.
        
         | musicale wrote:
         | > I am so shocked at how many people use `auto` in C++
         | 
         | Well I blame C++ for calling it "auto" in the first place.
         | Fortunately this is easily fixed:                   #define let
         | auto         #define var auto
         | 
         | ;-)
        
         | paulddraper wrote:
         | > it is a tedious exercise to figure out something's type, to
         | go look up its function, to see what type that is returning
         | 
         | To cite your previous sentence, why don't you use your IDE?
         | 
         | Or is this a magnetized needle sort of situation.
        
       | emcell wrote:
       | every time i see stuff like this, I hope I never have to work on
       | c++ projects again.
        
       ___________________________________________________________________
       (page generated 2025-01-11 23:01 UTC)