Auto is acceptable for iterators and, in some cases, references to complicated containers. Don't fucking type "auto pos" instead of "Vec2i pos", "Vec3f pos", or one of many other possible types that pos variable may represent.
1. Sometimes `auto` helps readability immensely. Sometimes it hurts immensely.
2. All generalizations about when and where to use it are generalizations and likely to start an argument.
3. Ultimately it's up to you and your diff reviewers to do the right thing. There will be people reading your code in the future and they will judge you and your decision, so choose wisely. Future-you included.
4. `auto` can often improve correctness/generality, not just readability, e.g. "int sz = v.size(); //BAD". No one writes "vector<int>::size_type sz = v.size();" I dare you to search your code for it.
5. The only controversial generalization I feel safe making, knowing saying it will get me into trouble, is that experienced non-C++11 (whether coming from C++03 or other languages) coders tend to be more apprehensive and cautious, using `auto` less than experienced C++11 programmers. Take it for what it's worth.
No, but plenty of people write "size_t sz = v.size();" which is equivalent (or superior) in the vast majority of real code. And in the rare situation where the return value of size() is not an unsigned integral type, obscuring that fact with auto is bad form.
The sensible generalization for auto is to use it to avoid specifying ugly template specializations when they are both clear from the context and otherwise unavoidable. So "auto it = x.find(f);" is clearly superior to "std::unordered_map<foo, bar, foobar_hash>::iterator it = x.find(f);" etc.
The real danger with auto is to use it when you don't know what the real type actually is, which is what people are tempted to do. But if you type "auto sz = v.size();" when you've specified some unusual template arguments that cause size() to return uint16_t, you're now obscuring the unusually small width of sz which could plausibly lead to integer overflow.
2. Agree
3. Agree
4. I don't use vector<int>::size_type and I don't use decltype(sz) throughout the rest of the function. I use size_t and let the compiler give me an error if size() happens to return a different type or, if when making use of sz, types get mixed.
5. That's a little too anecdotal and 'appeal to authority' for my liking.
The big question, I suppose, is defining guidelines for when auto helps readability and when it hurts it. My experience has been that it is best to default to no auto and only use auto after trying the non-auto way first. Iterators and template-heavy containers are great instances where auto helps. In our codebase I can't think of another situation in which auto would be useful.
We also don't have heavy template usage outside of containers. If your code has templates everywhere then I can imagine the number of times auto is an improvement in readability would be larger.
There have been exceptionally few times where I have jumped into someone else's code and said "wow, their heavy use of auto has made this easier to read and understand".
'There have been exceptionally few times where I have jumped into someone else's code and said "wow, their heavy use of auto has made this easier to read and understand".'
I think it's far easier to notice when you hit unreadable code and what seems to be making it unreadable, than it is to notice when you hit readable code and what seems to be making it unusually readable. That's not to say your conclusions are necessarily wrong (or right), by any means - just that I'd view this particular argument with an added measure of skepticism.
For point 5 where do you find these experienced C++11 programmers that aren't coming from earlier flavours of C++?
It's possible that I misunderstood what you read so in that case could you please clarify what you meant?
I just added links to several of Herb Sutters articles (e.g., http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-... ) and Scott Meyers talk (http://vimeo.com/channels/ndc2014/97318797 )
They say it better than I really could, and they say it very emphatically. I think the general consensus amongst the C++ leadership is to use auto when possible, for more reasons than I had considered.
edit: I notice you also object to 'appeals to authority'. In this case I am not saying do it because all of the known C++ authority tell you to do it, but indicating that they /also/ recommend it, hopefully adding more weight to the argument
Also, what's wrong with appealing to authority on difficult subject matter? Whats the point of attending Scott Meyers' lectures at all if you aren't going to heed his advice???
Nearly 100% of the time, you know what the type of a local variable is (otherwise people would be unable to use Python/Ruby/Javascript/etc.), so it's no great harm to readability. Furthermore, you are probably already working with an IDE that can tell you the type of a variable when you hover over it, anyway. I think that it also leads you to think more about interfaces than specific types.
auto bla = obj->GetBla();
bla[x] = y;
The assignment adds a new element or overwrites an existing element to the map, it will never crash.
Now during a refactoring obj->GetBla() is changed to return a vector<int>. In conventional code the build would break with an incompatible type error, the coder investigates, sees what's wrong and fixes the problem. With the auto statement the code would compile, but the bla[x] = y; would very likely not do what was intended (it can overwrite foreign memory or produce a segfault).
Is this not seen as a problem? I do use auto here and there where the type is obvious (for instance written on the right-hand-side of the assignment), but using 'auto wherever possible' is terrible advice IMHO, since it completely undermines type safety.
[edit: typos and formatting]
I do agree that jumping into a new code base can be slightly more trickier if type inference is used, but it's a minor point compared to the benefits in my opinion.
If you cherry pick something like Vec2i, type inference doesn't look that good, but the benefits are clearer from more complex code.
The example posted is a horrible use of autocomplete.
>const int calculated = [&] { auto l = lock(); auto first = stage1(); auto second = stage2(); return combine(first, second); }()
No idea what that does. It takes 2 variables of some type and does something too them. There is some sort of lock there too. I guess.
return combine( stage1(), stage2() )
bother you too? because that infers the return-types of stage1() and stage2() as well.Why don't I use an IDE to read it? No, how about why doesn't the original coder write the code for other people to read, instead of forcing me to use an entire IDE just to piece together what they're doing, like I'm some kind of code archaeologist.
I'm debugging this thing across a flaky modem line through which I'm getting an echoed terminal, I've managed to get gdb running and narrowed it down to a handful of suspicious lines of code, and the gimboid who wrote it assumed that everyone who ever looked at it would actually be using Visual Studio 20-whatever with all the plug-ins, rather than plain text a line at a time. Brilliant.
[1] http://www.stroustrup.com/4th.html [2] http://shop.oreilly.com/product/0636920033707.do
It's interesting and readable. I used to keep a listing of all the typos and errors I found but that project got buried beneath other things in my stack.
auto x = foo();
Suppose you refactor your code, and in the process, the return type of foo() changes. Without the use of auto, the compiler would produce an error message, alerting you that the above line of code may need your attention. As it is, with the use of auto, the above line will compile no matter what. In other words, that particular line now behaves as if C++ was an untyped language. Isn't that a step down as far as type-safety is concerned? Please note: I'm not asking here whether that's good or bad. I'm asking, "Assuming that we use the auto keyword throughout, can we still call C++ 'strongly typed'"? foo = [1, 2, 3]
x = foo
We didn't give a type to x, but the compiler will infer it anyway. If foo changes, x will change too. This does not make Haskell untyped, it just means it can add type annotations by itself in most cases (see Hindley-Milner type inference).Note that that code won't "compile no matter what", because in C++ construction using "=" is already an operation that not all types have. So if foo returns an object which does not have a copy constructor or move constructor, then that line will fail to compile. In the more general case, you'll still not be able to use operations on x that x's type does not support, and that's what type safety means.
Of course it is also possible to introduce stuff like this during refactoring without changing the type, but the more information the compiler has to find possibly regressions, the better.
You're correct that this is unsafe, but this isn't what auto does. You'd be assigning a variable x of type A an object of type A. Then what that code theoretically changes, you're now assigning variable x of type B an object of type B. auto just handles all the boilerplate. It will be type safe, it's just logically incorrect.
For example
public int foo()
{
return 1;
}
auto x = foo();
//is the same as
int x = foo();
And then you change it to this: public float foo()
{
return 1.0f;
}
auto x = foo();
//is the same as
float x = foo();
In either case you can go on to do stuff like 'float y = x / 10;' Because they're both valid operators. But there can be logical inconsistencies, like the integer division problem that will occur if x is an int. Then again, you probably shouldn't use auto for something as simple as int or float. But 'auto foobar = new vector<int>;' is a very clear statement, and if you replace vector with something else, odds are the compiler will throw a fit with any later methods you attempt to call.I haven't written C++ in a while, so I hope I didn't butcher the examples too much. I simply typed them into the comment box.
The definition of type-safe could be extended to mean what you say, it's not really a term that is set in stone. But I've always used type safety to mean preventing type-based syntax errors, not semantic errors. If it's more common to do otherwise, I'd be happy to be corrected.
For one thing, duck typing is always vulnerable to code that is syntactically correct but semantically nonsense. For example,
employee.fire()
and nuclear_missile.fire()
have quite different meanings, but if all you write is auto x = get_an_x()
x.fire()
then nothing in the language or compiler will stop you or notice the changed implication in this scenario.C++ is also vulnerable to a slightly more sinister variation because many conversions can be made implicitly, and sometimes those conversions are lossy. For example, suppose x was converted from an integral value to a floating point one because get_an_x needed was upgraded to offer more precision. Unfortunately, all the code you've got that later compares x to a known value using == might now be broken (comparing floating point values without a suitable tolerance) and again depending on how you've got your compiler's warnings set up you might never notice.
If C++ had a stronger type system, then at least the latter issue would be less dangerous, but with the language we're talking about today I think over-use of auto is a risk that shouldn't be ignored in code reviews.
It depends on how you code, I suppose. I use var very frequently in C# now after being averse to it at first. I've never run into an issue where var caused problems in my code (rarely my IDE won't be able to pick up on what type it should be, but it compiles fine).
If you're changing a variable between compatible types, like a parent class, or changing to a different type of enumerable class where usage is exactly the same, you don't really run into issues. If you're changing the entire usage of a class (like from a vector to an array, or something even more different) than you probably should not simply re-declare the variable, you should be rewriting that code.
You wouldn't change
vector<int> numbers = new vector<int>;
to int* numbers = new int[512];
Without making accommodating changes to the following code, would you? Then you shouldn't change auto numbers = new vector<int>;
to auto numbers = new int[512];
Without making changes to the rest of the code either. It's exactly the same as before, except you get to type less and it clutters the screen less when you don't need to write types multiple times on the same line. Especially for very long types names.And if you use "x" in some way that isn't allowed by the new type, you will still get a compiler error; although the error will be on the line where you use x and not on the line where you declare x. The compilers I've used (Visual C++, GCC and Clang) will tell you where x was declared in the error message.
Yeah well I'd rather not rely on 'chance' - if I would, I wouldn't be using C++.
auto lambda = [](auto x, auto y) {return x + y;};
That's progress of a sort, but in Haskell, a language designed to support this style of programming, it looks like this: lambda = (+)
Obviously C++ is slowly being left behind by modern programming language design and the collective experience of the programming industry over several decades since C++ was invented. Eventually it will be superseded by other languages that do support more expressive language features and better designed standard libraries (which is easy to do with the wisdom of hindsight, of course -- this is not a criticism of the people who built C++ without the benefit of that hindsight).But since we don't yet have any such alternatives that don't also have significant drawbacks and/or unacceptable run-time performance characteristics, for the near future C++ is still going to be the first choice for a lot of projects in the real world. In that respect, closing the loopholes and fixing the pain points is useful for a lot of people, and very wisely that seems to be what the standards committee are concentrating on doing.
[1] http://en.wikipedia.org/wiki/C%2B%2B14#New_language_features
&v[0] (without the parens) is even shorter :)
Following https://gobyexample.com and http://rustbyexample.com, a http://cppbyexample.com kind of site would be quite useful for people who are familiar with coding and just want to experience some practical use cases in idiomatic C++ (threading, json parsing, etc).
* http://thbecker.net/articles/rvalue_references/section_01.ht...
* https://en.wikipedia.org/wiki/Copy_elision
* http://en.cppreference.com/w/cpp/language/eval_order
* https://akrzemi1.wordpress.com
* https://dl.dropboxusercontent.com/u/13100941/C%2B%2B11.pdf
Understanding the combination of copy elision and move semantics (when and how they are applied) can require one to read the standard.
C++11 is not simpler than older C++, but using it is easier, if you have a good understanding of it.