
I hate the C++ keyword auto - ingve
http://www.randygaul.net/2015/12/06/i-hate-the-c-keyword-auto/
======
Animats
Well, the first bug is at

    
    
        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.

~~~
Ono-Sendai
let is traditionally used for expressions, not types.

~~~
klodolph
What do you mean? Here, "auto" is being used to define a variable, just like
the way "let" does in other languages.

    
    
        for (let i = results.begin( ); i != results.end( ); ++i)
    

This matches the use of "let" in Haskell, Lisp, Rust, ES6, etc.

(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.)

~~~
Ono-Sendai
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.

------
RogerL
As I just posted to his blog:

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);
        }

~~~
Skunkleton
There is still no indication what "employee" is, or what will happen when it
is passed to the "StoreEmpoyeeInFoo" function.

~~~
theyoungestgun
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.

~~~
pasas
How does autocomplete and tooltips help you when diffing/merging code? It
doesn't.

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.

~~~
Aleman360
> How does autocomplete and tooltips help you when diffing/merging code? It
> doesn't.

Sounds like there's an opportunity for better diff tools.

~~~
dysfunction
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.

------
72deluxe
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.

~~~
mratzloff
_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++:

[http://www.amazon.com/A-Tour-C-In-
Depth/dp/0321958314/ref=pd...](http://www.amazon.com/A-Tour-C-In-
Depth/dp/0321958314/ref=pd_sim_14_7?ie=UTF8&dpID=51xVLzxX4vL&dpSrc=sims&preST=_AC_UL160_SR119%2C160_&refRID=0MZ49N2C37W2JJ7E4179)

~~~
72deluxe
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).

------
kstenerud

        struct Results
        {
            char** entries;
            int entryCount;
        };
    
        Results results = SortByName( input );
    
        for ( int i = 0; i < results.entryCount; ++i )
        {
            char* entry = results.entries[ i ];
            // ...
        }
    

Really? You're going to use bare double pointers? Did I miss the DeLorean?

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.

~~~
sharpneli
> 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.

~~~
kstenerud
> 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".

------
incepted
> 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.

~~~
santaclaus
> 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.

------
moomin
Pop Quiz, what's the difference between

    
    
        auto x = y;
    

and

    
    
        ConcreteClass x = y;
    

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.

------
wyldfire
> compile-time "surprises" ... bugs the crap out of me .. . No unanticipated
> code.

s/I hate the C++ keyword auto/I hate C++/

~~~
raverbashing
This

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.

~~~
wyldfire
Sorry, "this" is a reserved word. ;)

~~~
raverbashing
this;

should do nothing (inside a class method) ;)

------
makecheck
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.

~~~
qMopey
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.

~~~
stinos
Probably because otherwise you cannot choose between stripping out references,
or not doing that (by using auto& for instance)?

------
chris_wot
Stroustrop explains his reasoning for the auto keyword here:

[http://www.stroustrup.com/C++11FAQ.html#auto](http://www.stroustrup.com/C++11FAQ.html#auto)

    
    
      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];
          // ...
      }

------
theseoafs
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.

~~~
slavik81
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.

------
pasas
+1 on this - it's _really_ annoying when overused.

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.

~~~
wtetzner
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?

~~~
pasas
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:

auto passengers = getPassengers();

~~~
andresmanz
Woah, that line immediately reminds me of JavaScript at work, and one of the
reasons why I don't like it.

------
sklogic
I wonder what degree of a butt-burn will this guy get from a typical Haskell
or ML code.

------
xgbi
Isn't this just a guy that has nearly no experience in C++? It is evident that
the for() loop uses iterators.

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?

------
theyoungestgun
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.

------
Ace17
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?

~~~
meshko
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.

------
leni536
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?

------
kabdib
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.

------
forgotmypassw
I guess he wouldn't get along with members of the "almost always auto"
religion.

------
MichaelMoser123
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.

------
circlingthesun
I try to avoid auto because it robs me of autocomplete.

~~~
omgtehlion
get a real IDE

