150

I saw a conference by Herb Sutter where he encourages every C++ programmer to use auto.

I had to read C# code some time ago where var was extensively used and the code was very hard to understand—every time var was used I had to check the return type of the right side. Sometimes more than once, because I forgot the type of the variable after a while!

I know the compiler knows the type and I don’t have to write it, but it is widely accepted that we should write code for programmers, not for compilers.

I also know that is more easy to write:

auto x = GetX();

Than:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

But this is written only once and the GetX() return type is checked many times to understand what type x has.

This made me wonder—does auto make C++ code harder to understand?

Jon Purdy
  • 20,547
Mircea Ispas
  • 1,633
  • Checking the type of x does not seem to be easier than checking the return type of GetX, does it? – Kiril Kirov Dec 20 '12 at 14:02
  • 4
    @KirilKirov But it is when GetX() is in another file and you have to jump from one file to another. – Mircea Ispas Dec 20 '12 at 14:03
  • 31
    Do you really need to check the return type every time? Why isn't the type clear from the code? auto can often makes things harder to read when they are already hard to read, i.e., functions too long, variables poorly named, etc. On short functions with decently named variables, knowing the types should be one of #1 easy or #2 irrelevant. – R. Martinho Fernandes Dec 20 '12 at 14:03
  • @Felics - most of the good editors, have keyboard shortcuts for "jump to declaration/definition". And shortcuts for "back". That would mean, that you can check the return type within 2 fast steps. Also, most good editors show the javadoc + the function declaration, when you move the mouse pointer over the function, you need to call. – Kiril Kirov Dec 20 '12 at 14:06
  • 26
    The "art" of using auto is a lot like determining when to use typedef. It's up to you to determine when it hinders and when it helps. – ahenderson Dec 20 '12 at 14:07
  • 19
    I thought I had the same problem, but then I realized that I can just understand the code without knowing the types. e.g.: "auto idx = get_index();" so idx is something holding an index. What the exact type is, is quite irrelevant for most cases. – PlasmaHH Dec 20 '12 at 14:37
  • 31
    So don't write auto x = GetX();, pick a better name than x that actually tells you what it does in that specific context ... that's often more useful than its type anyway. – Jonathan Wakely Dec 20 '12 at 14:46
  • 3
    With all of the other ridiculous crap that's in the C++ language, I highly doubt that type inference can make it any more unreadable! ;) – Mason Wheeler Dec 20 '12 at 21:30
  • 1
    I don't do a lot of programming in C++, but I've never found type inference to be a hindrance in other languages. Haskell and Scala both use it extensively. I also find myself using var regularly in C#, especially in cases like "var dict = new Dictionary<...>()". – KChaloux Dec 21 '12 at 16:06
  • 12
    If using more type inference makes it hard for a programmer to read the code, either the code or the programmer needs serious improvement. – C. A. McCann Dec 21 '12 at 16:29
  • Just use typedef-shortcuts when types are crucial for understanding code and auto, when they're obvious. – Dmytro Sirenko Dec 26 '12 at 23:18
  • Many programmers will find type information useless altogether. And limiting it to some degree would not harm even if you're into static typing. – SK-logic Dec 27 '12 at 12:12
  • 4
    "I had to check the return type of the right side" -- There's no need to know the types of things, just what they represent, which is best done through good names. – Jim Balter May 20 '13 at 19:34
  • 3
    Besides code readability and other considerations, to me 'auto' also takes away the ease of code browsing in IDEs like Visual Studio or SlickEdit. When the type is explicitly listed, I can get to the type definition with few clicks/keystrokes. – Radim Cernej Jul 01 '16 at 21:11
  • 1
    Just encode the type into the name of x using Hungarian notation. – Pukku Nov 07 '17 at 09:09
  • 1
    Using 'auto' breaks the autocomplete/intellisense in Visual Studio. Still. In 2020. – Andy Krouwel Oct 29 '20 at 22:23

14 Answers14

116

It's a case-by-case situation.

It sometimes makes code harder to understand, sometimes not. Take, for instance:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

is definitely easy to understand and definitely easier to write than the actual iterator declaration.

I've been using C++ for a while now, but I can guarantee that I'd get a compiler error at my first shot at this because I'd forget about the const_iterator and would initially go for the iterator... :)

I'd use it for cases like this, but not where it actually obfuscates the type (like your situation), but this is purely subjective.

  • 47
    Exactly. Who the heck cares about the type. It's an iterator. I don't care about the type, all I need to know is that I can use it to iterate. – R. Martinho Fernandes Dec 20 '12 at 14:05
  • 6
    +1. Even if you did name the type, you'd name it as std::map<int, std::string>::const_iterator, so it's not as if the name tells you much about the type anyway. – Steve Jessop Dec 20 '12 at 14:25
  • 6
    @SteveJessop: It tells me two things at least : the key is int, and the value is std::string. :) – Sarfaraz Nawaz Dec 20 '12 at 14:27
  • 16
    @Nawaz: and that you can't assign to it->second since it's a const iterator. All of which information is a repeat of what's in the previous line, const std::map<int, std::string>& x. Saying things multiple times does occasionally inform better, but by no means is that a general rule :-) – Steve Jessop Dec 20 '12 at 14:31
  • 1
    Also note that having a properly named x would make knowing the type here even less interesting. At the end of the day, I think that responsible use of auto leads to much better names and thus more readable code. – R. Martinho Fernandes Dec 20 '12 at 14:33
  • 11
    TBH I'd prefer for (anX : x) to make it even more obvious we're just iterating over x. The normal case where you need an iterator is when you're modifying the container, but x is const& – MSalters Dec 20 '12 at 17:05
  • 1
    I've only started writing iterator loops since auto. – Martin Beckett Dec 20 '12 at 22:47
  • 1
    The other interesting thing about that loop is that it works for any x with forward iterators. It doesn't matter if x is an array, a list, a map, whatever. If all you've decided at this point is that x will be a container containing a specific type of object, you can just write this and not come back to it. – David Thornley Dec 21 '12 at 22:51
  • 1
    imo the example should use cbegin() and cend() as it's (sort of) more const-correct and because it clarifies the intent better especially in the light of auto: it tells 'hey I'm a const iterator' and you don't even have to look at the definition of x to know it is. – stijn Jan 15 '13 at 20:46
  • then the next point is that the code would be made even more generic by using the non-member std::cbegin|end(), as those can work for plain arrays too, which is especially useful when writing templates, but could equally work if you changed the argument to a reference-to-array type. (then there's a more general argument to prefer non-members wherever possible, though I don't really grasp all the angles of that one) – underscore_d Dec 09 '17 at 15:48
  • @stijn no, it's totally fine to use begin() and end(). The intention is "give me this range", which is what you get. Now that we have std::as_const we don't need cbegin etc. – Caleth Oct 05 '18 at 08:41
116

Short answer: More completely, my current opinion on auto is that you should use auto by default unless you explicitly want a conversion. (Slightly more precisely, "... unless you want to explicitly commit to a type, which nearly always is because you want a conversion.")

Longer answer and rationale:

Write an explicit type (rather than auto) only when you really want to explicitly commit to a type, which nearly always means you want to explicitly get a conversion to that type. Off the top of my head, I recall two main cases:

  • (Common) The initializer_list surprise that auto x = { 1 }; deduces initializer_list. If you don’t want initializer_list, say the type -- i.e., explicitly ask for a conversion.
  • (Rare) The expression templates case, such as that auto x = matrix1 * matrix 2 + matrix3; captures a helper or proxy type not meant to be visible to the programmer. In many cases, it's fine and benign to capture that type, but sometimes if you really want it to collapse and do the computation then say the type -- i.e., again explicitly ask for a conversion.

Routinely use auto by default otherwise, because using auto avoids pitfalls and makes your code more correct, more maintainable and robust, and more efficient. Roughly in order from most to least important, in the spirit of "write for clarity and correctness first":

  • Correctness: Using auto guarantees you’ll get the right type. As the saying goes, if you repeat yourself (say the type redundantly), you can and will lie (get it wrong). Here's a usual example: void f( const vector<int>& v ) { for( /*…* -- at this point, if you write the iterator’s type explicitly, you want to remember to write const_iterator (did you?), whereas auto just gets it right.
  • Maintainability and robustness: Using auto makes your code more robust in the face of change, because when the expression's type changes, auto will continue to resolve to the correct type. If you instead commit to an explicit type, changing the expression's type will inject silent conversions when the new type converts to the old type, or needless build breaks when the new type still works-like the old type but doesn't convert to the old type (for example, when you change a map to an unordered_map, which is always fine if you aren't relying on order, using auto for your iterators you'll seamlessly switch from map<>::iterator to unordered_map<>::iterator, but using map<>::iterator everywhere explicitly means you'll be wasting your valuable time on a mechanical code fix ripple, unless an intern is walking by and you can foist off the boring work on them).
  • Performance: Because auto guarantees no implicit conversion will happen, it guarantees better performance by default. If instead you say the type, and it requires a conversion, you will often silently get a conversion whether you expected it or not.
  • Usability: Using auto is your only good option for hard-to-spell and unutterable types, such as lambdas and template helpers, short of resorting to repetitive decltype expressions or less-efficient indirections like std::function.
  • Convenience: And, yes, auto is less typing. I mention that last for completeness because it's a common reason to like it, but it's not the biggest reason to use it.

Hence: Prefer to say auto by default. It offers so much simplicity and performance and clarity goodness that you're only hurting yourself (and your code's future maintainers) if you don't. Only commit to an explicit type when you really mean it, which nearly always means you want an explicit conversion.

Yes, there is (now) a GotW about this.

Deduplicator
  • 9,031
Herb Sutter
  • 1,342
  • 14
    I find auto useful even when I do want a conversion. It allows me to explicitly ask for a conversion without repeating the type: auto x = static_cast<X>(y). The static_cast makes it clear that the conversion is on purpose and it avoids compiler warnings about the conversion. Normally avoiding compiler warnings isn't so good, but I'm OK with not getting a warning about a conversion that I considered carefully when I wrote static_cast. Though I wouldn't do this if there are no warnings now but I want to get warnings in future if the types change in a potentially dangerous way. – Bjarke Hammersholt Roune Dec 26 '12 at 18:05
  • 6
    One thing I find with auto is that we should strive to program against interfaces (not in the OOP sense), not against specific implementations. It's the same with templates, really. Do you complain about "hard to read code" because you have a template type parameter T that is used everywhere? No, I don't think so. In templates too we code against an interface, compile-time duck-typing is what many people call it. – Xeo Dec 27 '12 at 13:09
  • 9
    "Using auto guarantees you’ll get the right type." Not true at all. It only guarantees you'll get the type prescribed by some other part of your code. Whether that's right or not is entirely unclear when you hide it away behind auto. – Lightness Races in Orbit Mar 10 '15 at 19:06
  • 2
    I am really surprised that nobody cares about IDEs... Even modern IDEs do not correctly support jumping to class/struct definition in case of auto variable, but almost all of them do it correctly with explicit type specification. Nobody uses IDE? Everybody uses only int/float/bool variables? Everybody prefers external documentation for libraries instead of self-documented headers? – avtomaton Jan 26 '17 at 01:15
  • that GotW: https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/ I don't see how that "initializer_list surprise" is a surprise; braces around = RHS don't make much sense by any other interpretation (bar braced init-list, but you need to know what you're initialising, which is an oxymoron with auto). The one that is surprising is auto i{1} also deducing initializer_list, despite implying not take this braced init-list but rather take this expression and use its type... but we get initializer_list there too. Thankfully, C++17 fixes all this up well. – underscore_d Dec 09 '17 at 15:43
  • I disagree with this answer, and I’d be curious to know if you’ve changed your stance in the meantime (and might thus want to rewrite the answer). Your answer currently states that you wouldn’t use auto when you want to perform a conversion. Why is that? You’d be giving up syntactic uniformity. I prefer using auto even in these situations (despite the added verbosity), and perform explicit coercion (either via a cast or uniform initialisation). So in the situations above I’d write, respectively, (1) e.g. auto x = int8_t{1};, and (2) auto x = matrix{matrix1 * matrix 2 + matrix3};. – Konrad Rudolph Sep 24 '19 at 13:12
  • 2
    This answer lists reasons in favour of using 'auto', but it doesn't address Mircea's point on readability. – Max Barraclough Apr 22 '20 at 12:36
  • I think Herb probably moved on a long time ago, but I'll comment anyway. I like his work, but I have to disagree here. I like duck typed programming. I enjoy using Python and I actually have quite a big soft spot for Objective-C, and personally I think method calls and OOP are best in duck typed languages. I like how templates let me do duck typing. However, I personally I am on board for "auto when it makes sense", I think "almost always auto" is a shocking oversimplification. Search SO for auto mistakes and problems, there's plenty... – jrh Feb 23 '21 at 20:12
  • 1
  • "Using auto guarantees you’ll get the right type" no, it automatically matches the type at time of compiling the function and spreads that type throughout your code. You're not finding the right type, you're delegating that job to somebody else. You've now got a "type dependency", your code is now going to change along with that thing that you used auto on. If that's what you want, great, but I think you should make it clearer what you're signing up for with this. It has its own gotcha situations.
  • – jrh Feb 23 '21 at 20:18
  • "the new type still works-like the old type but doesn't convert to the old type", I can sympathize with the annoyance of having to edit old code when you or I know full well that it's really the same thing, I really can. In this case I think Duck Typing really shines! However I would say this is more of a bad OOP design problem, why isn't there a base class I can use that has the same iterator behavior? The answer is probably because the STL wasn't designed for it, which is fair, but not everyone is maintaining a 20 year old library.
  • – jrh Feb 23 '21 at 20:21
  • "Implicit conversions" -- it's true that auto itself will not implicitly convert (which is great), but if you attempt to do something with the auto variable (like assign an auto double to an int), an implicit conversion may happen at that point anyway and it may be harder to find it. – jrh Feb 23 '21 at 20:23
  • "Unutterable types": Yes, absolutely use auto for these. "Hard to spell types", using is IMO a much better option, not just for the sake of saving typing but also making self-documenting code, you can make a context or application specific using that informs the reader exactly what the type means, instead of sweeping it under the rug. If it's hard to type out, it's probably also hard for the reader to understand, so personally I'd appreciate a using over (or in addition to) auto.
  • – jrh Feb 23 '21 at 20:25