Auto

I am deeply suspicious of auto and decltype. I acknowledge that I am on the opposite side of the argument to people such as Bjarne Stroustrup, Herb Sutter and Scott Meyers. This is not a comfortable place to be. I picked these three luminaries because it is easy to find quotes from them – I am sure there are plenty of other people who agree with them.

Unless there is a good reason not to, use auto in small scopes

Bjarne Stroustrup The C++ Programming Language 4th edition, 6.3.6.1

Almost Always Auto

Herb Sutter Almost Always Auto

Prefer auto to explicit type declarations

Scott Meyers Effective Modern C++ (Early release) Item 5

This is not an attempt to trash any of these authors – I have quoted their conclusions however, they have well thought through reasons backing up those conclusions. I highly recommend reading the entire pieces that I took these quotes from – as always with Stroustrup, Sutter and Meyers, even when I disagree with their conclusions (which isn’t that often) I learn something from their arguments. All three authors talk about the problems that can arise from auto – note that even in the quotes I have picked there are phrases like “unless there is a good reason not to”, “almost always” and “prefer”.

I think my main disagreement is with the weighting given to some of the disadvantages. My objection to auto can be summarized as “makes code easier to write, makes it harder to read”, and I weight this very highly, as someone who has spent a fair amount of his career trying to get up to speed with large codebases written by someone else, and trying to get new team members up to speed with large codebases written partially by me.

For example, I joined a team with a large, pre-existing, pre-C++11 codebase that used compiler extensions to fake auto and decltype. Learning the codebase was made far more difficult by the absence of explicitly named types in many functions. The code would not have compiled unless all the types were compatible, that’s good, however I want to know more than that. I want to know what the types are so that I can go and look them up and find out what else they can do for me. I want to look at a function and know what types are the same and what aren’t. Sooner or later I am going to make changes to that function – the changes I can make will be constrained by what the types let me do (and if the types constrain me too much I am going to have to either extend the types or use different types). I am saying “types” over and over again – they’re a big deal.

I want to know what type everything is, and I weight that desire very highly.

There will be IDEs which will make it easy to discover the types being deduced, however as Scott Meyers acknowledges in Item 4 of “Effective Modern C++” they don’t do a perfect job (although I assume that will improve), and not all of us are in a position to use IDEs anyway.

Type deduction is not new in C++ – we’ve been using templates for years, and I have fewer objections to using templates. I am trying to work out why I find templates more acceptable than auto.

Reason #1 has to be that I am more used to templates than I am to auto. It takes me time to get used to a new feature. However, there is more to it than that.

Template types still have a name. Imagine this introduction to a template class:

template< typename ITERATOR >

Within the class we don’t know exactly what the type of ITERATOR is, however the name gives us a strong hint that it should be some sort of iterator.

The fact that the template types have a name also lets us see what relation (if any) there is between types in a piece of code. Contrast this:

auto i( someFunction() );
auto j( someOtherFunction() );

with

ITERATOR i( someFunction() );
ITERATOR j( someOtherFunction() );

The template version makes it clear that i and j are the same type.

On the one hand I dislike the argument “this feature can be misused so we shouldn’t ever use it”, on the other hand I have already seen this feature be massively misused, and I suspect that it will be misused more often than not. Herb Sutter writes:

Remember that preferring auto variables is motivated primarily by correctness, performance, maintainability, and robustness – and only lastly about typing convenience.

Herb Sutter Almost Always Auto

I think this is fine advice, I think that everyone in the world should stick to it. I do not believe that they will. In my experience (and I really hope that other people have different experiences), saving some typing often outweighs everything else. Delayed gratification is an unknown concept in many programming shops.

With more experience I might come around to auto. There are certainly people who have thought about this more deeply than I have who are in favour of auto. There are also definitely places where auto will help in template code. I know I dismissed the “reduces typing” argument earlier, but it would be really nice to avoid have to type typename quite so often.


A little more follow up on the team using the compiler extension versions of auto and decltype. Their use of auto was problematic, it was also exacerbated by a number of other factors. The team loved macros and loved token pasting (##). There were many classes whose declaration and definition were created using macros, and the name of the class was generated using token pasting. Combine that with their use of auto and there were classes whose full name never appeared in the code. They were impossible to grep for and I doubt that there were many IDEs (which that team wasn’t using anyway) that would have helped. I did not last very long on that team.

8 thoughts on “Auto

  1. Carl D says:

    I have had very analogous discussions with my colleagues about the use of var in C# code, with the same arguments being raised there.

    In GotW #94 Herb hits on one of the key distinctions of mindset: “Although lack of commitment may be a bad thing in other areas of life, not committing to a specific type is often desirable by default in reusable code.”

    The key here is “in reusable code”. I’ve stated to colleagues that after 30+ years of writing code I’ve come to believe that all code should be written with the assumption that it WILL be reused. Even those little throw-away programs get re-hashed, sliced and diced and re-used at some level.

    If code will be reused, then minimizing dependencies of all sorts is A Good Thing.

    As a primarily C# developer I do have the advantage of using an IDE that can give me high quality feedback about the types of things – when I care. If I were writing in a non-IDE environment, I might feel differently, but I don’t think so.

    It is no doubt the case that the quality, texture and structure of the code base in which you’re working can have a huge influence on the importance of knowing the exact type of a variable. In well-written code that was created with the intention that it will be reused, I’ve found that I really don’t miss having that exact type – I’m better off not knowing it.

    • Bob says:

      In well-written code that was created with the intention that it will be reused, I’ve found that I really don’t miss having that exact type

      I think this is a key point, and perhaps the real lesson from my example is that if the rest of the code is badly written you’re in big trouble anyway.

  2. Hi!

    I teach to expert programmers, most often people involved in AAA videogames. I have noticed a fear/distrust of auto on their part, at least at first, in almost every new group I have had the pleasure to work with. Having moved my own PhD codebase from C++03 to C++11 over time, I can understand their impression, but I have also noticed a few things:

    – auto is more natural in small functions that do one thing well than it is in large functions that try to do too much in one place. The context provides the intent, and auto lets us think in algorithmic terms more than in terms such as «what container is this, and should this be an iterator or a const_iterator?»

    – this property of auto also helps in code maintenance, where algorithms become less sensitive to implementation details that should probably remain abstract

    – of course, when implementation details count, there’s nothing stopping us from being more explicit about the intent. If a function really requires O(1) distance computation, we can static_assert on iterator_category, just as we can insist on the container being a vector of something if this is what we care about

    – we also have to know enough about type deduction to understand when to use auto, auto&, const auto& or decltype 🙂

    With respect to C#’s var, mentioned by Carl D, I think one of the reasons why it is not used as much as it could is the IDE, which (in case of Visual Studio at least) auto-completes often tedious «new» statements when the type of a reference is explicit. In the case of C++ and auto, we now have a tendency to create objects with emplace or with make functions, where the lassert explicits the type on the right side of the assignment operation; that tends to make auto clear from context anyway (read: auto x = f() ; is quite abstract, but auto x = make_unique(…) is much clearer). That’s one of Sutter’s points, from my understanding: given T x{args} and auto x = T{args}, I don’t really think auto makes anything more obscure.

    One might also note that Forwarding References (cppcon 2014-born term here), where the compiler essentially deduces proper types for function arguments, is another step in that «less redundant typing effort». It’s a bit too greedy in some cases, but there’s something there.

    We must remain awake and aware of what our code is doing. With small functions with well-defined mandates, and with make functions that tend to optimize object initialization, auto works very well. It helps us concentrate on algorithmic essentials. And I think it makes for a good default case; it just does not do the job all the time, and we still have to know things about our types, for example where we need to be const-correct or when references should be used instead of values. There’s not substitute for thinking.

    • Bob says:

      small functions with well-defined mandates

      Amen to that. Short functions are nearly always a good idea, and I can see how they would help with auto enormously.

  3. John Payson says:

    I would suggest that “var” in C# is appropriate (and would, if allowed, even be appropriate for fields) in cases where the right-hand side is one of several forms:

    – A typecast to a particular type, in which case the inferred type would be the same as the typecast.
    – An expression of the form “new someType(…)” or “default(someType)”, in which case the inferred type would likewise be apparent.
    – An expression of the form “someClass.staticMethod(…)”, where the method’s return type is the same as the name of the class upon which it is invoked; if the compiler were to only allow this form when the return type is as indicated, that would make the return type here apparent.

    I’m not sufficiently familiar with C++ to know whether similar rules would be suitable there, but I would not expect avoiding redundancy in those cases to degrade code legibility, since the type of the resulting expression would be readily apparent. In many cases, however, I would expect it to help code legibility by calling attention to cases where e.g. code declares a variable of a base type and initializes it with a new instance of a particular implementation.

  4. It seems to me that there’s something missing in between auto and an explicitly specified type. Using auto almost says to the reader of the code that the type can be anything, while if you specify the type explicitly you’re saying that it only works with exactly that type. It’s like an all or nothing situation. There’s no way to express that you want the compiler to infer the type, but that it must be a “function-like-thing” or “container-like-thing” or whatever (analogous to the run time feature of polymorphism, but purely a compile-time construct for type checking and writing clearer code and would not require the use of pointers or references).

    Or to put in terms of what Carl D said about committing to a type and how it affects reusability, if you had some kind of type constraints on what types you allow for a particular declaration you could commit to the weakest possible type-constraint that still makes sense for the intention of the code. This also makes sense to inform maintainers of deeper intentions. If you could specify what constraints your code is depending on, it would be a hint to future developers not to rely on the specific type but only on facets that are specified by the constraints. In other words, even if the actual type inferred by the compiler is “giraffe”, if you specified “animal” as your type constraint then you’re telling future maintainers not to use the fact that the animal happens to be have a long neck in this case, because if they did then they would be committing to a more specific type than intended and thus reducing the generality of the code.

    Currently the only option if you intend the declaration to be “any animal” seems to be to either declare it as “giraffe” (too specific, too constrained, and breaks compilation if you later it changes to infer “tiger” type instead) or to say “auto” (too generic, doesn’t communicate intentions, doesn’t break when you want it to – when it changes to infer “cabbage” type instead of an animal, or when you add dependencies you didn’t intend).

    • Bob says:

      if you had some kind of type constraints on what types you allow for a particular declaration

      This sounds a little like the concept proposal that didn’t make it into C++11. I agree that this can be useful, but there are times where I still just want to know what the type is.

      To paraphrase Jerry Maguire:

      Show me the type (or rather SHOW ME THE TYPE!)

      • Yes, in some ways it would be similar to the concept of C++ concepts, except that no templates would need to be involved any more than auto implies the use of templates.

        What I had in mind is something more like subtype polymorphism, except completely a compile-time concept. To go back to the animal/giraffe example, in today’s C++ you could have an abstract base class Animal, and a concrete derived class Giraffe, but you can’t do this:

        Animal myAnimal = Giraffe();

        because obviously it would slice giraffe object. You could do this:

        Animal* myAnimal = new Giraffe();

        but now you’re forced to use the heap, manage the memory, and function-calls to the Animal are now dynamically dispatched at run time. You can also do this:

        auto myAnimal = Giraffe();

        but now we loose the type information (in the sense that someone reading the code can’t see what type the variable is, and the compiler won’t type-check that Giraffe indeed constructs/returns an Animal). We can also do this:

        Giraffe myAnimal = Giraffe();

        now we’ve lost the generality and explicit intent etc, and made the code a little less flexible than it needed to be, and encouraging maintainers to incorrectly rely on the fact that myAnimal is a Giraffe.

        What I’m saying we need is something in between, like:

        auto Animal myAnimal = Giraffe();

        The code auto Animal means “Automatically infer the type, but it must be a subclass of Animal”. All calls to myAnimal are now statically dispatched etc, in the same way they would be if the coder had just said Giraffe instead of auto Animal. The intent of the code is clearer: myAnimal can be any animal, but not something that isn’t an animal. To me Animal is the type of that variable, at least as far as readability and static type checking is concerned. Yes, the static type (as far as the output machine code is concerned) is actually Giraffe, but you’re saying to readers of the code: you can rely on the fact that the rest of the code works perfectly well with any animal, and so feel free to substitute a different animal here when you’re doing maintenance. Also, since the rest of the code works with any animal, future maintainers can’t rely on the fact that it’s a giraffe (it should be a compile error to say myAnimal.longNeck... – which is different to how we see “auto” right now), so reusability and decoupling is enforced in a way that it couldn’t be if the original writer of the code just used the type Giraffe.

        So to answer “Show me the type!”, I’ll say “The type is Animal!”. I’m not permitting you to know it’s specifically a Giraffe any more than you’re allowed to know that some Animal* happens to point to a Giraffe (i.e. you can get to it by some ugly casts or something if you’re really determined, but please don’t do it because you’ll make the code brittle).

        Does that make more sense?

Leave a Reply

Your email address will not be published.