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