A few thoughts on type inference...What I have noticed in C++, is that partly because of implicit conversions between arithmetic types, people often don't know or care what types they actually deal with. If you look at typical C++ code, you won't find it annotated with f (float) or u (unsigned int) postfixes, because it's not strictly necessary if the type stands next to it. Many people don't even know that adding two shorts doesn't result in a short. Migrating code from
for (std::size_t i = 0; i < size; ++i)
to
for (auto i = 0; i < size; ++i)
however changes semantics in a non-obvious way. Mostly, this doesn't matter, but there are cases where it does -- especially when the type decides between different code paths, e.g. in overload resolution.
You're totally right that type inference can be extremely helpful, as it can reduce boilerplate code and maintenance in general, e.g. with simplified type migration across functions. This is especially nice when dealing with tempalte metaprogramming, where a lot of code is required to express simple things. But also for iterator declarations and other clutter where the details aren't interesting.
Propagating the type information across multiple indirections is a double-edged sword: it helps migrate types (changing it at the source will make clients adapt automatically), but it's also a source of error (one client behaves differently now). Also, it makes error messages more difficult to interpret, because the error may manifest completely elsewhere than its source. And it's not as if template metaprogramming error messages have been easy to read so far
what I mean is: types can act as an interface, and as such as a barrier between decoupled modules. Whatever happens on one side doesn't affect the other; a core principle in software development. And this is not just important from a design perspective; it's also something to consider given C++' header/implementation separation. Functions using type inference in their signature or return type are essentially templates and force the implementation to be located in the header.
While many people argue that type inference helps hide implementation/language details unimportant to the developer (the types), it's sometimes overlooked that in certain situations, types are
the central part of code. This applies less to generic template libraries, but more to algorithm-heavy programming, where the type decides what data structure is used and how efficient different operations on it are. For example, when dealing with STL containers, I'm usually interested if it's a vector or deque or list, but not in the verbatim declaration of its iterator.
What we are usually interested in are not types, as they're declared, especially because C++ with its generic programming paradigms makes them rather wordy. However, what I as a programmer am always interested in is the
type category, which expresses its semantics. Am I dealing with a container, with a number, with a string, with a pointer? C++ type inference is all-or-nothing, you hide the entire information associated to a type with it. Some can be compensated through expressive variable and function names, but not always.
Concepts are a big step in that direction. They're essentially what I'm referring to with
type categories: they express what a type is "like", i.e. provide a description of its capabilities and semantics, but don't dictate its concrete declaration.
That's why I would argue that for average C++ code (i.e. not heavy generic programming), a page full of auto keywords is more difficult to read than a page with mixed explicit and inferred types. Explicit where types are short and carry crucial semantics, inferred where they're verbose noise. Because even in the presence of IDEs and static analysis tools, one still has to hover over types, i.e. it's not as easy to see the big picture.
Anecdote: Yesterday, 7 years ago, I started a discussion about C++11 features, among others type inference, with some of the brightest C++ minds in the German speaking community. I was skeptical regarding some new features -- and while I've changed quite a few of my views after using those features, some of my anticipations have remained to this day.
I'm amazed again and again by the fact that C++11 is now half a decade old, and that we started talking about it already years before...