How about instead you use proper names? "Results" is a terrible name. So is "i". That is why you can't understand that code. Your 'fix' is still unreadable - the struct is rarely near the for loop, so you are going to have to go searching anyway to figure out what the hell is going on.
This is completely readable, IMO:
for (auto employee : sorted_employees) {
StoreEmpoyeeInFoo(employee);
}
Agreed - but this is why modern features should be paired with modern tools. A simple hover-over or click through in an IDE to show what the function signature looks like is pretty basic these days. Autocomplete on what that auto variable's API offers getting better and better as well.
I do not consider the API of a reasonably complex construct "obvious". That is often where IDEs shine for me - in java or any other language that can gather that info for me. It's scenarios where I don't have to go to cppreference.com, because the IDE is going to give me what I want directly.
I used to really be anal on making every single line explicit: no shortcuts. But over time, especially with increasing my usage of templates, I've come to relax on these things and prefer to put my efforts in good variable names and trusting the compiler.
I'm not saying you should use auto for basic types (especially ones that can be easily cast into each other - that shit gets dangerous). I am saying that auto is extremely powerful when working with most interesting APIs. The most recent example I have is a lot of things I end up doing with chrono.
There really is... diff tools that point to the compiler (or a parse tree for dynamically-typed languages) could be so much smarter than what we have now.
From my perspective this is fundamentally flawed logic. Why would you want to depend on a tool when you don't have to? Why would I want to hover over all sorts of auto variables to figure out their type when I could just read it instead?
If I'm not going to depend on the IDE to tell me what a deduced variable type is, you can bet your ass I'm going to want to depend on it for autocompleting some of the insanely long type names are: std::chrono::steady_clock::time_point gets old real quick - and so does stead_clock::time_point (if you want to include the namespace).
Speaking as someone who has done a lot of C++ work with nothing more than Emacs, I don't think there is any particular value or virtue in using only the sort of tools that were available in the 80s. The problem of finding out exactly what sort of thing you are dealing with did not start with auto (the elements of expressions have never been labeled with their type), and I think better tools are the way to the solution. C++ is actually a good language for this, as as little as possible is left to be decided at runtime.
With regard to the hovering issue specifically, there can be a great deal of visual clutter from type names that is a hinderance to understanding most of the time; now you only have to see it when you need it. That is only the start, however; a decent IDE should, for example, make it easy to go from there to the declaration of the type, should you want to.
Okay, if I replace auto, then you know that employee is of type "EmployeeRec".
Happy? Of course not. That still tells you basically nothing. Surely it does not tell you what the store function does (I argue 'storeXXX' is generally a bad name, the point is to use a good name for functions, I just threw boilerplate there).
Comments plus good names will tell you what is going on. Replacing 'auto' with 'std::vector<EmployeeRec>' for the most part, doesn't.
I'm not being dogmatic. If having the type there is important, then of course use the type, not auto. But it seems that everyone who writes a rant about auto shows us code with no comments, and terrible, meaningless variable and function names. Get that part right, and then, if you are still puzzled, and the code isn't meant to be generic, sure, put the type in. Why not?
Somehow I manage to write and read tons of dynamic code that has no types whatsoever. I agree, sometimes I would like a type there, and that is a perfect time to not use auto. But in general, we are trying to write code at a higher level. I mostly don't want to write, or think about
for (std::my_vector<some_long_type> i = blah.begin();
Sometimes I do, and in those cases I'll write it that way.
The fundamental issue pointed out in the post is an interesting one. It all begins with the assumption that with a language like C++, you can reason about things like performance without a lot of weird surprises.
Much of that reasoning begins with understanding the types involved.
Of course, I would argue if you wanted to complain about this, you should begin by rejecting operator overloading, which IMO is far more dangerous in this regard than the auto keyword. At least with auto, there's a visual indicator that something "magical" is going on, and if you want to really know the return type, you navigate to the function or whatever to figure out what that type is.
With operator overloads, code that looks totally innocuous can end up doing extremely surprising things.
There is another related dimension too: C++ is verbose for a reason and sometimes trying too hard to make a thing not what it is does harm. C++'s verbosity carries information that has value.
Personally I tend to not even use typedef's as often as other programmers do. I do it for really verbose stuff at times but I prefer always seeing what a type actually is in most cases. I find that it actually makes code more readable, though it does make refactoring slightly more annoying.
It's absolutely true that in an object instantiation, declaring the type twice (in the variable declaration and again in the construction) is repetitive. And it's a flaw in all object-based algol-style programming languages, which is why the var keyword was introduced to C#, and the diamond operator (while a half-assed solution for only part of the problem) was added to Java.
But that's just one narrow use case where I see this feature being worthwhile.
Operator overloading would be fine if everybody had some standard for how to employ it sensibly. Unfortunately, the C++ standard library (over)uses it poorly all over the place, so it's really not setting an example of moderation.
This is a common sentiment I think, but operator overloading, at least in the context of numerical analysis, has perfectly sensible standards already, as in Matlab, R, python, julia, and the like, yet C++ under-uses it and doesn't even provide anything overloadable for matrix-multiplication (like `.* ` vs `* `) or a linear solve `\` operator. C is even worse, since you can't even define arithmetic operators for a custom numerical type.
That's true if you're only looking at arithmetical operators. C++ most often overloads assignment and indexing operators. You can even overload the cast operator, and if that isn't confusing and unnecessary... just don't ever use std::vector<bool>.
Aside from vector<bool>, which pretty much everyone agrees is a failed experiment, where do you think operator overloading overused in the standard library?
for ( auto i = results.begin( ); i < results.end( ); ++i )
which should be
for ( auto i = results.begin( ); i != results.end( ); ++i )
This will work on all the standard collection types. If "results" is some non-ordered type, such as a list, the first form won't work.
The reason for "auto" is that writing the type expression for an iterator is bulky, and quite messy in some situations involving generics. There were some situations where you needed "decltype". That was just too obscure.
Newer languages offer auto-type implicit type declaration by default. Both Go and Rust do this. It makes programs easier to write, but harder to read.
When this idea was first being kicked around on the C++ standards newsgroup, I proposed that the keyword "let" should be used. But adding a new keyword might break existing code, so "auto", which wasn't used much, was repurposed.
What I mean is that for me, 'auto' in C++ means something like 'automatic type', e.g. it stands in for the actual type, whereas let x = some_expr means something else - it doesn't really say anything directly about the type of x.
Writing code that breaks if someone changes the type of the collection is not a bug in any sense that I ever saw the word used in. At most it's a failure to make the code as pliable as one might desire it to be - but the author is not very likely to value the ability to easily replace an array with a list (it appears he'd be concerned about the performance implications and would want to review all the relevant code anyway.)
Also in reality making sure that your arrays can be replaced with lists means you can't store indexes into the array but instead you must store iterators, which sucks when you have to declare them (auto won't work in many contexts and typedef will expand into the ugly underlying type in many contexts), and sucks when you have to print them ("5" or "342" reads better than whatever garbage you'll have to resort to when printing out an iterator, and it persists better between program runs and across platforms, too.) So if I have an array/vector I'd rather not try to pretend that it's a "non-ordered collection" until I absolutely ought to use random access; I'd just treat it as an array/vector from the start.
So I guess what I'm trying to say is, not only is it not a bug but the attitude of people who write such non-bugs has many advantages.
(And BTW caring about performance is not the main point. A nice thing about your typical dynamic language is that there's not much gratuitous polymorphism. You get a few basic collection types and you use these and most code doesn't jump through hoops to be able to replace the standard collection type with some polymorphic replacement only satisfying a subset of the interface. So-called "type-rich" programming gets gnarly, I prefer "type-poor" on any day. Which isn't the main point, either, the main point is that things that work nicely in practice (like treating arrays as arrays) matter more to me than things that sound "right" from an abstract viewpoint (like being able to replace your collection types with other types.) And calling it a "bug" is, I guess it kinda bothers me and hence the wall of text...)
If you use ".end()" on an C++ standard array collection, what you get is not the array size. You get an invalid iterator pointing one element past the end of the array, which tells you when to stop.
Most newer languages have built-in syntax for "iterate over collection". C++ does not, leading to somewhat clunky constructs like this. It's ugly, but that's where C++ ended up.
(I personally think that the C++ committee went way too far in making the template fanatics happy. It's neat that C++ templates, being a general term-rewriting system, allow arbitrary computation at compile time, but templates are a terrible programming language. More recently, it worries me that Rust is going too far in that direction.)
> And why do you need to know the kind of assembly being generated? How much smarter than the compiler are you?
The compilers are actually quite stupid in the end. One has to know what kind of constructs the compiler can work well with and then change the problematic code to such that the compiler can produce efficient code out of.
This step comes after the profiler has pointed out the problematic spot.
Personally I find C++ code written in the modern style far more difficult to optimize than one that's closer to C. And yeah, in soft realtime this does matter. It's the difference between 20 and 60fps. It can easily be even the difference between 0.1 and 60fps.
> One has to know what kind of constructs the compiler can work well with and then change the problematic code to such that the compiler can produce efficient code out of.
Great! And luckily C++ allows you to do this. Once you've profiled and found the bottlenecks, you can get as low level as you need to solve it.
Far better to remain idiomatic until you find a problem rather than painting yourself into a bunch of low level architectural corners with no justification other than "it might be slow".
It's great when writing code, but really annoying when trying to understand code written by someone else using it and then having to bounce round code to work out what a function's actually returning.
Don't you have to bounce around code to figure out what the TypeDef refers to anyway? Instead of using a well named TypeDef, wouldn't you get the same effect with a well named variable?
Sometimes, but PassengerVec at least gives you a lot more indication (including whether it's a pointer or reference as they have to be declared along with it) than:
He acknowledges its real power when talking about templates. That's what this feature is about: make template metaprogramming accessible, and the regular developers of the world will be more inclined to make use of the features.
I don't think that `auto` is what you hate; what you hate is operator overloading.
It's impossible to tell what all those operators are doing because operators are endlessly overloadable in C++. If you make all type declarations manually then that's still only part of the issue solved: the next step is that you have to go look at the class to find out what the overloaded operators do.
Auto without overloading has none of those same faults.
I don't find operators to be any different than regular functions. Usually I can tell what they do based on their name. Occasionally I do need to look at the class to see what they do.
Vector math in C++ would really suck without operator overloading, so I'm glad it exists.
This is dangerous advice.
The same rant could be made about named structs, overloaded operators, or virtual functions.
"With these stupid named structs now I can't know the offsets of the members I'm using!"
"Now I don't know anymore which function is being called!"
These are abstraction tools, allowing yourself to emancipate from low-level details, like memory layout and generated code.
If you need fine-grained low-level control over the code generation, why use a C++11 compiler at all?
named structs and overloaded operators are very similar to auto in terms of poor benefit to risk ratio. Virtual functions are more intuitive and solve a problem.
> since this code is operating on purely opaque data.
Not opaque, generic. Genericity is good.
This rant is typical from someone who's still thinking very low level and who hasn't spent any time upgrading his tools to embrace the new level of abstraction that modern language concepts offer.
Not sure what type an expression is? Ask your IDE. This is the kind of things that tools are very good at, so let them take care of this while you focus on writing clean, generic and reusable code.
> This rant is typical from someone who's still thinking very low level and who hasn't spent any time upgrading his tools to embrace the new level of abstraction that modern language concepts offer.
OP is an undergrad whose public projects look like vanilla reimplementations of Box2D, so this isn't too surprising in that light.
I think the problem is the apparent lack of familiarity with STL containers, and the ubiquitous prevalence of .begin(). end() and knowing that ++ against an iterator will advance it. I looked at the for loop and it was obvious that it was a container of some sort that offered a ForwardIterator.
The replacement code is a struct that uses a pointer to a char array with no constructor or destructor. That is far more dangerous.
What the heck is "char * *" meant to be? Who owns the data inside it? How does it get cleaned up? What is its initial value? (clue: absolute garbage).
eg. Results x;
then read x.entries - KABOOM.
This is horrible C-style code. The problem is nothing to do with auto. The problem is no obviously-defined lifetimes of data and throwing pointers around as a solution.
EDIT: I heartily recommend the first couple of chapters of Stroustrup's C++ Programming Language Fourth Edition, where suddenly everything makes sense and you never want to see naked new, delete or double pointers ever again. Use references everywhere, and ensure you have sensible constructors and destructors.
I have to deal with crappy code like this all day in a brand-new C++ project where my superior pulls confused faces when expressions like RAII, copy-constructor, iterator, const-correctness, references etc. are used. I am pretty cheesed off with it and hate seeing code like this any more, particularly in brand new code (littered with uninitialised variables, double-lists with new items pushed onto them so nobody has a clue who owns items, C-style casts etc. etc. I weep).
Life in C++ land is now simple: Use the STL. Don't cast unless you have to. Use references. Use const correctness. Let the compiler do the work for you. Let it check for you. Don't make life hard for yourself.
I heartily recommend the first couple of chapters of Stroustrup's C++ Programming Language Fourth Edition, where suddenly everything makes sense and you never want to see naked new, delete or double pointers ever again. Use references everywhere, and ensure you have sensible constructors and destructors.
That is essentially the content of his short book, A Tour of C++:
Ah yes but with the full book you get to look up the various concepts further on in the book if you don't "get it" within the intro section. There are times where ideas are introduced within the intro section but are tersely explained, where reading a section in the later chapter will make lightbulbs turn on in my head and it made sense.
Of course, the "Tour of C++" book is great as well. But the full thick book is fantastic. Nice font in it compared to the Third Edition too (kiss goodbye to serif fonts).
The difference, of course, is that y wasn't actually of type ConcreteClass and you've just performed a potentially incorrect or inefficient cast.
auto isn't perfect in this regard, though. auto x = y where y is ConcreteClass& makes x ConcreteClass, not ConcreteClass& (there are reasons for this, but it's still a potential source of nasty perf bugs).
And yeah, you're going to have to relearn how to reason about code. This isn't a bad thing.
The use of auto to deduce the type of a variable from its initializer is
obviously most useful when that type is either hard to know exactly or hard to
write. Consider:
template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
In C++98, we'd have to write
template<class T> void printall(const vector<T>& v)
{
for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
When the type of a variable depends critically on template argument it can be
really hard to write code without auto. For example:
template<class T, class U> void multiply(const vector<T>& vt, const vector<U>& vu)
{
// ...
auto tmp = vt[i]*vu[i];
// ...
}
What are some good debug practices to track auto type deduction? I know this trick:
template<class... Args>
struct check_type; //Don't define it!
...
auto x=something();
checktype<decltype(x)>(); // Compiler: "error: invalid use of incomplete type ‘struct check_type<actual type of x>’"
Also there is a nice boost type pretty print runtime library. Else?
It's all fun and games until the type of the thing you're iterating becomes 'unsigned int' (because you finally ran into a case where you overflowed 2^31-1) and suddenly need to track down all the places you sinned.
Been there. It sucks, especially when customers are down and people are yelling at you.
It's funny, none of his examples point out the simplest problem with "auto": it is not a reference and in fact it strips references out. Therefore, if something non-trivial is being used, most programmers might say "auto var = value" (as his examples do) and incur costs or unexpected behavior that would not be seen with the expression "const auto &var = value".
And that is the real problem with C++: its defaults are downright stupid in many situations and the only real way to produce code without pitfalls is to know all the tricks.
I don't think "auto" is meant to be overused. It makes no sense to use it in place of an "int". It is fantastic for eliminating redundancy in expressions where the type already exists in multiple places and the "auto" serves to eliminate one of the pointless type expressions. It is also good when using objects that you really shouldn't depend on the types of, such as lambdas and possibly magical object types in certain template libraries that exist primarily as a means to an end.
Current compilers appear to generate very confusing error messages for "auto" variables, too. For instance, if you've forgotten to include "Xyz.h" which defines the class "Xyz", and the type of your "auto x = f()" happens to be Xyz, and class Xyz hasn't been used anywhere else in your code, it may take quite awhile to figure out that this is the problem. Therefore, resist the urge to blindly remove all type information from your code; keep as many hints around as you can.
Good point! OP here, and this was one of the worst things (imo) when dealing with auto. Why the hell would it strip off reference qualifiers??? No clue man, no clue.
auto is bad? Auto helps to reduce the amount of typing on repeating the type declaration for the left hand side of an assignment, that can't be bad. Assignment to left hand side declared with auto doesn't do anything wicked like implicit type conversion - so its ok in my book.
69 comments
[ 2.2 ms ] story [ 112 ms ] threadHow about instead you use proper names? "Results" is a terrible name. So is "i". That is why you can't understand that code. Your 'fix' is still unreadable - the struct is rarely near the for loop, so you are going to have to go searching anyway to figure out what the hell is going on.
This is completely readable, IMO:
Relying on IDEs to give you insight into what should be obvious by just looking at the code show's that something's wrong IMO.
I used to really be anal on making every single line explicit: no shortcuts. But over time, especially with increasing my usage of templates, I've come to relax on these things and prefer to put my efforts in good variable names and trusting the compiler.
I'm not saying you should use auto for basic types (especially ones that can be easily cast into each other - that shit gets dangerous). I am saying that auto is extremely powerful when working with most interesting APIs. The most recent example I have is a lot of things I end up doing with chrono.
Sounds like there's an opportunity for better diff tools.
If I'm not going to depend on the IDE to tell me what a deduced variable type is, you can bet your ass I'm going to want to depend on it for autocompleting some of the insanely long type names are: std::chrono::steady_clock::time_point gets old real quick - and so does stead_clock::time_point (if you want to include the namespace).
With regard to the hovering issue specifically, there can be a great deal of visual clutter from type names that is a hinderance to understanding most of the time; now you only have to see it when you need it. That is only the start, however; a decent IDE should, for example, make it easy to go from there to the declaration of the type, should you want to.
Happy? Of course not. That still tells you basically nothing. Surely it does not tell you what the store function does (I argue 'storeXXX' is generally a bad name, the point is to use a good name for functions, I just threw boilerplate there).
Comments plus good names will tell you what is going on. Replacing 'auto' with 'std::vector<EmployeeRec>' for the most part, doesn't.
I'm not being dogmatic. If having the type there is important, then of course use the type, not auto. But it seems that everyone who writes a rant about auto shows us code with no comments, and terrible, meaningless variable and function names. Get that part right, and then, if you are still puzzled, and the code isn't meant to be generic, sure, put the type in. Why not?
Somehow I manage to write and read tons of dynamic code that has no types whatsoever. I agree, sometimes I would like a type there, and that is a perfect time to not use auto. But in general, we are trying to write code at a higher level. I mostly don't want to write, or think about
Sometimes I do, and in those cases I'll write it that way.Much of that reasoning begins with understanding the types involved.
Of course, I would argue if you wanted to complain about this, you should begin by rejecting operator overloading, which IMO is far more dangerous in this regard than the auto keyword. At least with auto, there's a visual indicator that something "magical" is going on, and if you want to really know the return type, you navigate to the function or whatever to figure out what that type is.
With operator overloads, code that looks totally innocuous can end up doing extremely surprising things.
Personally I tend to not even use typedef's as often as other programmers do. I do it for really verbose stuff at times but I prefer always seeing what a type actually is in most cases. I find that it actually makes code more readable, though it does make refactoring slightly more annoying.
It's absolutely true that in an object instantiation, declaring the type twice (in the variable declaration and again in the construction) is repetitive. And it's a flaw in all object-based algol-style programming languages, which is why the var keyword was introduced to C#, and the diamond operator (while a half-assed solution for only part of the problem) was added to Java.
But that's just one narrow use case where I see this feature being worthwhile.
Write/Read using << and >>. Not a major problem, but it's weird and not very useful when you're trying to be C compatible.
You can index into a std::deque, but not a std::queue.
I'd also count the need for explicit in constructor declarations to be a similar kind of mistake.
The reason for "auto" is that writing the type expression for an iterator is bulky, and quite messy in some situations involving generics. There were some situations where you needed "decltype". That was just too obscure.
Newer languages offer auto-type implicit type declaration by default. Both Go and Rust do this. It makes programs easier to write, but harder to read.
When this idea was first being kicked around on the C++ standards newsgroup, I proposed that the keyword "let" should be used. But adding a new keyword might break existing code, so "auto", which wasn't used much, was repurposed.
(Note that when I say "variable" in this context, I am using it as the hypernym, not in the sense where it denotes only mutable variables.)
Also in reality making sure that your arrays can be replaced with lists means you can't store indexes into the array but instead you must store iterators, which sucks when you have to declare them (auto won't work in many contexts and typedef will expand into the ugly underlying type in many contexts), and sucks when you have to print them ("5" or "342" reads better than whatever garbage you'll have to resort to when printing out an iterator, and it persists better between program runs and across platforms, too.) So if I have an array/vector I'd rather not try to pretend that it's a "non-ordered collection" until I absolutely ought to use random access; I'd just treat it as an array/vector from the start.
So I guess what I'm trying to say is, not only is it not a bug but the attitude of people who write such non-bugs has many advantages.
(And BTW caring about performance is not the main point. A nice thing about your typical dynamic language is that there's not much gratuitous polymorphism. You get a few basic collection types and you use these and most code doesn't jump through hoops to be able to replace the standard collection type with some polymorphic replacement only satisfying a subset of the interface. So-called "type-rich" programming gets gnarly, I prefer "type-poor" on any day. Which isn't the main point, either, the main point is that things that work nicely in practice (like treating arrays as arrays) matter more to me than things that sound "right" from an abstract viewpoint (like being able to replace your collection types with other types.) And calling it a "bug" is, I guess it kinda bothers me and hence the wall of text...)
Most newer languages have built-in syntax for "iterate over collection". C++ does not, leading to somewhat clunky constructs like this. It's ugly, but that's where C++ ended up.
(I personally think that the C++ committee went way too far in making the template fanatics happy. It's neat that C++ templates, being a general term-rewriting system, allow arbitrary computation at compile time, but templates are a terrible programming language. More recently, it worries me that Rust is going too far in that direction.)
Huh? C++11 includes a range for:
And why do you need to know the kind of assembly being generated? How much smarter than the compiler are you?
If you want to find poorly performant code, use a profiler.
The compilers are actually quite stupid in the end. One has to know what kind of constructs the compiler can work well with and then change the problematic code to such that the compiler can produce efficient code out of.
This step comes after the profiler has pointed out the problematic spot.
Personally I find C++ code written in the modern style far more difficult to optimize than one that's closer to C. And yeah, in soft realtime this does matter. It's the difference between 20 and 60fps. It can easily be even the difference between 0.1 and 60fps.
Great! And luckily C++ allows you to do this. Once you've profiled and found the bottlenecks, you can get as low level as you need to solve it.
Far better to remain idiomatic until you find a problem rather than painting yourself into a bunch of low level architectural corners with no justification other than "it might be slow".
It's great when writing code, but really annoying when trying to understand code written by someone else using it and then having to bounce round code to work out what a function's actually returning.
Well named TypeDefs are much better IMO.
auto passengers = getPassengers();
s/I hate the C++ keyword auto/I hate C++/
The problem is not the auto keyword. The problem is C++
And auto is a very welcome improvement. No I don't want to type SomeCollection<MyStuff> items = new SomeCollection<MyStuff>();
The compiler already knows what 'items' should be, I don't need to repeat myself.
should do nothing (inside a class method) ;)
It's impossible to tell what all those operators are doing because operators are endlessly overloadable in C++. If you make all type declarations manually then that's still only part of the issue solved: the next step is that you have to go look at the class to find out what the overloaded operators do.
Auto without overloading has none of those same faults.
Vector math in C++ would really suck without operator overloading, so I'm glad it exists.
Not opaque, generic. Genericity is good.
This rant is typical from someone who's still thinking very low level and who hasn't spent any time upgrading his tools to embrace the new level of abstraction that modern language concepts offer.
Not sure what type an expression is? Ask your IDE. This is the kind of things that tools are very good at, so let them take care of this while you focus on writing clean, generic and reusable code.
OP is an undergrad whose public projects look like vanilla reimplementations of Box2D, so this isn't too surprising in that light.
The replacement code is a struct that uses a pointer to a char array with no constructor or destructor. That is far more dangerous.
What the heck is "char * *" meant to be? Who owns the data inside it? How does it get cleaned up? What is its initial value? (clue: absolute garbage).
eg. Results x;
then read x.entries - KABOOM.
This is horrible C-style code. The problem is nothing to do with auto. The problem is no obviously-defined lifetimes of data and throwing pointers around as a solution.
EDIT: I heartily recommend the first couple of chapters of Stroustrup's C++ Programming Language Fourth Edition, where suddenly everything makes sense and you never want to see naked new, delete or double pointers ever again. Use references everywhere, and ensure you have sensible constructors and destructors.
I have to deal with crappy code like this all day in a brand-new C++ project where my superior pulls confused faces when expressions like RAII, copy-constructor, iterator, const-correctness, references etc. are used. I am pretty cheesed off with it and hate seeing code like this any more, particularly in brand new code (littered with uninitialised variables, double-lists with new items pushed onto them so nobody has a clue who owns items, C-style casts etc. etc. I weep).
Life in C++ land is now simple: Use the STL. Don't cast unless you have to. Use references. Use const correctness. Let the compiler do the work for you. Let it check for you. Don't make life hard for yourself.
That is essentially the content of his short book, A Tour of C++:
http://www.amazon.com/A-Tour-C-In-Depth/dp/0321958314/ref=pd...
https://isocpp.org/tour
It really is a very nice overview.
Of course, the "Tour of C++" book is great as well. But the full thick book is fantastic. Nice font in it compared to the Third Edition too (kiss goodbye to serif fonts).
auto isn't perfect in this regard, though. auto x = y where y is ConcreteClass& makes x ConcreteClass, not ConcreteClass& (there are reasons for this, but it's still a potential source of nasty perf bugs).
And yeah, you're going to have to relearn how to reason about code. This isn't a bad thing.
http://www.stroustrup.com/C++11FAQ.html#auto
Advocating for bare pointers and C-style solution is premature optimisation and blatantly wrong when using C++..
Finally, what is this post doing here? What do we learn from this?
Been there. It sucks, especially when customers are down and people are yelling at you.
And that is the real problem with C++: its defaults are downright stupid in many situations and the only real way to produce code without pitfalls is to know all the tricks.
I don't think "auto" is meant to be overused. It makes no sense to use it in place of an "int". It is fantastic for eliminating redundancy in expressions where the type already exists in multiple places and the "auto" serves to eliminate one of the pointless type expressions. It is also good when using objects that you really shouldn't depend on the types of, such as lambdas and possibly magical object types in certain template libraries that exist primarily as a means to an end.
Current compilers appear to generate very confusing error messages for "auto" variables, too. For instance, if you've forgotten to include "Xyz.h" which defines the class "Xyz", and the type of your "auto x = f()" happens to be Xyz, and class Xyz hasn't been used anywhere else in your code, it may take quite awhile to figure out that this is the problem. Therefore, resist the urge to blindly remove all type information from your code; keep as many hints around as you can.