Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question

migrated from stackoverflow.com Dec 20 '12 at 21:25

This question came from our site for professional and enthusiast programmers.

19  
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
15  
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
14  
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
22  
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
8  
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

12 Answers 12

up vote 86 down vote accepted

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.

share|improve this answer
30  
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
5  
+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
2  
@SteveJessop: It tells me two things at least : the key is int, and the value is std::string. :) –  Nawaz Dec 20 '12 at 14:27
13  
@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
6  
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

Readability is subjective; you'll need to look at the situation and decide what's best.

As you pointed out, without auto, long declarations can produce a lot of clutter. But as you also pointed out, short declarations can remove type information which may be valuable.

On top of this, I'd also add this: be sure you're looking at readability and not writeability. Code that's easy to write is generally not easy to read and vice versa. For instance, if I were writing, I'd prefer auto. If I were reading, maybe the longer declarations.

Then there's consistency; how important is that to you? Would you want auto in some parts and explicit declarations in others, or one consistent method throughout?

share|improve this answer

Yes, it makes it easier to know the type of your variable if you don't use auto. The question is: do you need to know the type of your variable to read the code? Sometimes the answer will be yes, sometimes no. For example, when getting an iterator from a std::vector<int>, do you need to know that it's a std::vector<int>::iterator or would auto iterator = ...; suffice? Everything that anybody would want to do with an iterator is given by the fact it's an iterator - it just doesn't matter what the type is specifically.

Use auto in those situations when it doesn't make your code harder to read.

share|improve this answer

This question solicits opinion, which will vary from programmer to programmer, but I would say no. In fact in many cases just the opposite, auto can help to make code easier to understand by allowing the programmer to focus on the logic rather than the minutiae.

This is especially true in the face of complex template types. Here is a simplified & contrived example. Which is easier to understand?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. or...

for( auto it = things_.begin(); it != things_.end(); ++it )

Some would say the second is easier to understand, others may say the first. Yet others might say that a gratuitous use of auto may contribute to a dumbing-down of the programmers that use it, but that's another story.

share|improve this answer
3  
+1 Haha, everyone is presenting std::map examples, additionally with complex template arguments. –  Nawaz Dec 20 '12 at 14:15
1  
@Nawaz: It's easy to come up with crazy-long template names using maps. :) –  John Dibling Dec 20 '12 at 14:17
    
@Nawaz: but I wonder why then no one is coming with range based for loops as the better and more readable alternative... –  PlasmaHH Dec 20 '12 at 14:41
    
@PlasmaHH, not all loops with iterators can be replace with range-based for e.g. if iterators are invalidated in the loop body and so need to be pre-incremented or not incremented at all. –  Jonathan Wakely Dec 20 '12 at 14:43
    
@PlasmaHH: In my case, MSVC10 doesn't do range-based for loops. Since MSVC10 is my go-to C++11 testbed, I don't really have much experience with them. –  John Dibling Dec 20 '12 at 14:44

Look at it another way. Do you write:

std::cout << (foo() + bar()) << "\n";

or:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

Sometimes it doesn't help to spell the type out explicitly.

The decision whether you need to mention the type isn't the same as the decision whether you want to split the code across multiple statements by defining intermediate variables. In C++03 the two were linked, you can think of auto as a way to separate them.

Sometimes making the types explicit can be useful:

// seems legit    
if (foo() < bar()) { ... }

vs.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

In cases where you declare a variable, using auto lets the type go unspoken just as it is in many expressions. You should probably try to decide for yourself when that helps readability and when it hinders.

You can argue that mixing signed and unsigned types is a mistake to begin with (indeed, some argue further that one should not use unsigned types at all). The reason it's arguably a mistake is that it makes the types of the operands vitally important because of the different behaviour. If it's a bad thing to need to know the types of your values, then it probably isn't also a bad thing not to need to know them. So provided the code isn't already confusing for other reasons, that makes auto OK, right? ;-)

Particularly when writing generic code there are cases where the actual type of a variable shouldn't be important, what matters is that it satisfies the required interface. So auto provides a level of abstraction where you ignore the type (but of course the compiler doesn't, it knows). Working at a suitable level of abstraction can help readability quite a lot, working at the "wrong" level makes reading the code a slog.

share|improve this answer
1  
+1. Different perspective than usual. –  Nawaz Dec 20 '12 at 14:20
16  
+1 auto allows you to create named variables with unnameable or uninteresting types. Meaningful names can be useful. –  Mankarse Dec 20 '12 at 14:27

Personally I use auto only when it's absolutely obvious for the programmer what it is.

Example 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Example 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns
share|improve this answer
4  
This is a clear example of bad naming hurting readability, not really auto. No one has the faintest idea what "DoSomethingWeird" does, so using auto or not won't make it any more readable. You will have to check the docs either way. –  R. Martinho Fernandes Dec 20 '12 at 14:10
1  
@R.MartinhoFernandes, that name was for the sake of example only. Is it better now? –  aleguna Dec 20 '12 at 14:13
2  
Ok, it's somewhat better now. I still find the variable is poorly named, though, which still hurts. Were you to write auto record = myObj.FindRecord(something) it would be clear that the variable type was record. Or naming it it or similar would make it clear it returns an iterator. Note that, even if you did not use auto, properly naming the variable would mean you don't need to jump back to the declaration to look at the type from anywhere in the function. I removed my downvote because the example isn't a complete strawman now, but I still don't buy the argument here. –  R. Martinho Fernandes Dec 20 '12 at 14:19
1  
To add to @R.MartinhoFernandes: the question is, is it really important now WHAT a "record" exactly is? It is I think more important THAT it is a record, the actual underlying primitive type is another abstraction layer.. So one would without auto probably have: MyClass::RecordTy record = myObj.FindRecord (something) –  paul23 Dec 20 '12 at 14:28
2  
@paul23: What does using auto versus the type gain you, then, if your only objection is "I don't know how to use this". Either makes you look it up anyway. –  GManNickG Dec 20 '12 at 19:31

IMO, you're looking at this pretty much in reverse.

It's not a matter of auto leading to code that's unreadable or even less readable. It's a matter of (hoping that) having an explicit type for the return value will make up for the fact that it's (apparently) not clear what type would be returned by some particular function.

At least in my opinion, if you have a function whose return type isn't immediately obvious, that's your problem right there. What the function does should be obvious from its name, and the type of the return value should be obvious from what it does. If not, that's the real source of the problem.

If there's a problem here, it's not with auto. It's with the rest of the code, and chances are pretty good that the explicit type is just enough of a band-aid to keep you from seeing and/or fixing the core problem. Once you've fixed that real problem, readability of the code using auto will generally be just fine.

I suppose in fairness I should add: I've dealt with a few cases where such things weren't nearly as obvious as you'd like, and fixing the problem was fairly untenable as well. Just for one example, I did some consulting for a company a couple years ago that had previously merged with another company. They ended up with a code base that was more "shoved together" than really merged. The constituent programs had started out using different (but quite similar) libraries for similar purposes, and though they were working to merge things more cleanly, they still did. In a fair number of cases, the only way to guess what type would be returned by a given function was to know where that function had originated.

Even in such a case, you can help make quite a few things clearer. In that case, all the code started out in the global namespace. Simply moving a fair amount into some namespaces eliminated the name clashes and eased type-tracking quite a bit as well.

share|improve this answer

I will take the point of less readable code as an advantage, and will encourage the programmer to use it more and more. Why? Clearly if the code using auto is difficult to read, then it will be difficult to write too. The programmer is forced to use the meaningful variable name , to make his/her job better.
Maybe in the beginning the programmer may not write the meaningful variable names. But eventually while fixing the bugs, or in code review, when he/she has to explain the code to others, or in not so near future, he/she explaining the code to maintenance people, the programmer will realize the mistake and will use the meaningful variable name in future.

share|improve this answer

Many good answers so far, but to focus on the original question, I do think Herb goes too far in his advice to use auto liberally. Your example is one case where using auto obviously hurts readability. Some people insist it is a non-issue with modern IDEs where you can hover over a variable and see the type, but I disagree: even people that always use an IDE sometimes need to look at snippets of code in isolation (think of code reviews, for instance) and an IDE won't help.

Bottom line: use auto when it helps: i.e. iterators in for loops. Don't use it when it makes the reader struggle to find out the type.

share|improve this answer

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 should be a GotW about this. This suffices for now. :)

share|improve this answer
8  
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
3  
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

I'm quite surprised that no one pointed out yet that auto helps if there is no clear type. In this case, you either work around this problem by using a #define or a typedef in a template to finding the actual usable type (and this is sometimes not trivial), or you just use auto.

Suppose you got a function, that returns something with platform-specific type:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Witch usage would you prefer?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

or just simply

auto stuff = getStuff();

Sure, you can write

#define StuffType (...)

as well somewhere, but does

StuffType stuff = getStuff();

actually tell anything more about x's type? It tells it is what is returned from there, but it is exactly what auto is. This is just redundant - 'stuff' is written 3 times here - this in my opinion makes it less readable than the 'auto' version.

share|improve this answer

I have two guidelines:

  • If the type of the variable is obvious, tedious to write or hard to determine use auto.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
    
  • If you need specific conversion or the result type is not obvious and might cause confusion.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious
    
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.