The last time I worked meaningfully with C++ was back in 2013. Now that I write mostly Rust and TypeScript, I'm amazed by how C++ has changed over the years!
Regarding the "auto" in C++, and technically in any language, it seems conceptually wrong. The ONLY use-case I can imagine is when the type name is long, and you don't want to type it manually, or the abstractions went beyond your control, which again I don't think is a scalable approach.
Type inference is actually very useful you just need to have the right amount, too little and most of your time is spent on bureaucracy, too much and the software is incomprehensible.
In both Rust and C++ we need this because we have unnameable types, so if their type can't be inferred (in C++ deduced) we can't use types at all.
In both languages all the lambdas are unnameable and in Rust all the functions are too (C++ doesn't have a type for functions themselves only for function pointers and we can name a function pointers type in either language)
I always try to avoid auto in c++ or var in C#. On the paper it is a nice was to save you from typing out the type but this is only true if you have tool support and can use your mouse to obtain the type. In printouts or even in a text snippet I’m am lost. I think auto inC++ was the try to shorten some overburden type constructs and make it a little more friendly to use heavy templating. Please forgive my bad English. I’m no native speaker
Very much true. On the other hand web based review interfaces seem to be stuck in the '60, when they could be so much better if they properly integrated with the compiler.
I came to like auto over the years, although I also use it sparingly.
Sometimes the concrete types only add visual noise and not much helpful information, e.g. iterators:
auto it = some_container.begin();
Not even once have I wished to know the actual type of the iterator.
This is indeed exactly correct. Probably on its own this is the most important reason for most people to use it, as I think most of the millions of C++ developers in the world (and yes there are apparently millions) are not messing with compiler flags to get the checks that probably should be there by default anyway. The keyword auto gives you that.
I knew the answer to most of these intuitively but the story isn’t great. Regardless of the programming language, I’ve always been an “auto” minimalist. There are relatively few contexts where relying on inference is justified by the expedience. Ignoring the issues raises by the article, explicitness simplifies things and reduces bugs.
That said, there are some contexts in which “auto” definitely improves the situation.
You will typically find bugs go up when you do not use auto in most average C++ code bases because, as noted by another comment, a lot of C++ developers have the bad habit of writing uninitialized variables, which auto prevents.
This makes things seem more complicated than they already are, I feel?
There's nothing special about auto here. It deduces the same type as a template parameter, with the same collapsing rules.
decltype(auto) is a different beast and it's much more confusing. It means, more or less, "preserve the type of the expression, unless given a simple identifier, in which case use the type of the identifier."
Auto has really made c++ unapproachable to me. It's hard enough to reason about anything templated, and now I frequently see code where every method returns auto. How is any one supposed to do a code review without loading the patch into their IDE?
Same in Java and Kotlin, but then again, the majority of people who write code wouldn't understand what I mean when I say "whether a collection is returned as List or Set is an important part of the API 'contract'".
I'd suppose this really depends on how you are developing your codebase but most code should probably be using a trailing return type or using an auto (or template) return type with a concept/requires constraint on the return type.
For any seriously templated or metaprogrammed code nowadays a concept/requires is going to make it a lot more obvious what your code is actually doing and give you actually useful errors in the event someone is misusing your code.
Generally, you don't. I'm not sure why the parent suggested you should normally do this. However, there are occasional specific situations in which it's helpful, and that's when you use it.
One of the craziest bugs I have had in C++ was due to auto, and it would only trigger when Trump would announce tariffs. It would have been completely preventable if I had paid full attention to my IDE feedback or the correct compiler flags were set.
Saving this to use as an argument when C++ comes up in a discussion. This toxic swamp cannot be fixed and anyone chosing to jump into it needs a warning.
Most of the relevance of this is limited to C++ library authors doing metaprogramming.
Most of the "ugly" of these examples only really matters for library authors and even then most of the time you'd be hard pressed to put yourself in these situations. Otherwise it "just works".
Basically any adherence to a modicum of best practices avoids the bulk of the warts that come with type deduction or at worst reduces them to a compile error.
I see this argument often. It is valid right until you get first multipage error message from a code that uses stl (which is all c++ code, because it is impossible to use c++ without standard library).
Inference is a constraint solving problem: the type of a variable depends on all the ways it is used. In C++, deduction simply sets the type of a variable from its initializing expression.
95%[1] of the time, deduction is enough, but occasionally you really wish you had proper inference.
Type deduction is a form of type inference, a very restricted/crude form of type inference that only considers the type of the immediate expression. The term is used in C++ because it predates the use of auto and was the term used to determine how to implicitly instantiate templates. auto uses exactly the same rules (with 1 single exception) as template type deduction, so the name was kept for familiarity. If instead of initializing a variable, you went through the examples on this website and passed the expression into a template function, the type would be deduced in exactly the same way with the exception of initializer lists.
Type inference is usually reserved for more general algorithms that can inspect not only how a variable is initialized, but how the variable used, such as what functions it's passed into, etc...
> Type inference is usually reserved for more general algorithms that can inspect not only how a variable is initialized, but how the variable used, such as what functions it's passed into, etc...
In a modern context, both would be called "type inference" because unidirectional type inference is quite a bit more common now than the bidirectional kind, given that many major languages adopted it.
If you want to specify constraint-based type inference then you can say global HM (e.g. Haskell), local HM (e.g. Rust), or just bidirectional type inference.
Type inference is when you try to infer a type from its usage. ``auto`` does no such thing, it just copies a known type from source to target. Target has no influence over source's type.
The same general concepts often have different terminology between different languages. For example, which is better, a parent class or a super class? Or a method function or a member function? Initializer or constructor? Long list of these synonyms.
Regarding the "auto" in C++, and technically in any language, it seems conceptually wrong. The ONLY use-case I can imagine is when the type name is long, and you don't want to type it manually, or the abstractions went beyond your control, which again I don't think is a scalable approach.
In both Rust and C++ we need this because we have unnameable types, so if their type can't be inferred (in C++ deduced) we can't use types at all.
In both languages all the lambdas are unnameable and in Rust all the functions are too (C++ doesn't have a type for functions themselves only for function pointers and we can name a function pointers type in either language)
IDEs are an invention from the late 1970's, early 1980's.
So for example I'd write:
Because writing: Feels very redundantWhereas I'd write:
Because it's not obvious what the type is otherwise ( Some IDEs will overlay hint the type there though ).Although with newer language features you can also write:
Which is even better.auto it = some_container.begin();
Not even once have I wished to know the actual type of the iterator.
For example:
auto a;
will always fail to compile not matter what flags.
int a;
is valid.
Also it prevents implicit type conversions, what you get as type on auto is the type you put at the right.
That's good.
That said, there are some contexts in which “auto” definitely improves the situation.
There's nothing special about auto here. It deduces the same type as a template parameter, with the same collapsing rules.
decltype(auto) is a different beast and it's much more confusing. It means, more or less, "preserve the type of the expression, unless given a simple identifier, in which case use the type of the identifier."
For any seriously templated or metaprogrammed code nowadays a concept/requires is going to make it a lot more obvious what your code is actually doing and give you actually useful errors in the event someone is misusing your code.
Most of the "ugly" of these examples only really matters for library authors and even then most of the time you'd be hard pressed to put yourself in these situations. Otherwise it "just works".
Basically any adherence to a modicum of best practices avoids the bulk of the warts that come with type deduction or at worst reduces them to a compile error.
95%[1] of the time, deduction is enough, but occasionally you really wish you had proper inference.
[1] percentage made up on the spot.
Type inference is usually reserved for more general algorithms that can inspect not only how a variable is initialized, but how the variable used, such as what functions it's passed into, etc...
In a modern context, both would be called "type inference" because unidirectional type inference is quite a bit more common now than the bidirectional kind, given that many major languages adopted it.
If you want to specify constraint-based type inference then you can say global HM (e.g. Haskell), local HM (e.g. Rust), or just bidirectional type inference.
https://news.ycombinator.com/item?id=11604474