154

It seems that auto was a fairly significant feature to be added in C++11 that seems to follow a lot of the newer languages. As with a language like Python, I have not seen any explicit variable declaration (I am not sure if it is possible using Python standards).

Is there a drawback to using auto to declare variables instead of explicitly declaring them?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
DxAlpha
  • 1,433
  • 2
  • 9
  • 13
  • 1
    See also: http://stackoverflow.com/questions/6434971/how-much-is-too-much-with-c11-auto-keyword, http://stackoverflow.com/questions/15254461/when-is-it-appropriate-to-use-auto-in-c, http://stackoverflow.com/questions/6900459/the-new-keyword-auto-when-should-it-be-used-to-declare-a-variable-type, http://stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous, and perhaps others. Not sure if these are exact duplicates, but they're definitely candidates. – Cody Gray - on strike Jan 13 '16 at 04:47
  • 3
    The only downside I found was when I had to port a code base to a (console) platform whose compiler didn't support (and had no intention of supporting) C++11 features! – Sam Jan 13 '16 at 05:39
  • 7
    Just for completeness GotW #94 "Almost Always Auto": http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/ – Richard Critten Jan 13 '16 at 06:57
  • 1
    I was listening to cppcast and there was mention of clash b/w auto and list initializers. I'll try to find that podcast. – Abhinav Gauniyal Jan 14 '16 at 09:27
  • 2
    the first downside I think is impacting the readability of the code – ggrr Jan 21 '16 at 02:03
  • 1
    You should accept an answer DxAlpha. – gsamaras Apr 01 '16 at 12:44
  • I'd argue that `auto x = 10` is a bad idea, since you will have no idea whether the compiler made `int` or `short`, ..etc, since different compilers can result in differences (compiler arguments too), especially if the code depends on specific datatype limitation – Khaled.K Sep 29 '20 at 09:06

14 Answers14

122

The question is about drawbacks of auto, so this answer highlights some of those. A drawback of using a programming language feature (in this case, a facility associated with a language keyword) does not mean that feature is unacceptable, nor does it mean that feature should be avoided entirely. It means there are disadvantages along with advantages, so a decision to use auto type deduction over alternatives must consider engineering trade-offs.

When used well, auto has several advantages as well - which is not the subject of the question. The drawbacks result from ease of abuse, and from increased potential for code to behave in unintended or unexpected ways.

The main drawback is that, by using auto, you don't necessarily know the type of object being created. There are also occasions where the programmer might expect the compiler to deduce one type, but the compiler adamantly deduces another.

Given a declaration like

auto result = CallSomeFunction(x,y,z);

you don't necessarily have knowledge of what type result is. It might be an int. It might be a pointer. It might be something else. All of those support different operations. You can also dramatically change the code by a minor change like

auto result = CallSomeFunction(a,y,z);

because, depending on what overloads exist for CallSomeFunction() the type of result might be completely different - and subsequent code may therefore behave completely differently than intended. You might suddenly trigger error messages in later code(e.g. subsequently trying to dereference an int, trying to change something which is now const). The more sinister change is where your change sails past the compiler, but subsequent code behaves in different and unknown - possibly buggy - ways. For example (as noted by sashoalm in comments) if the deduced type of a variable changes an integral type to a floating point type - and subsequent code is unexpectedly and silently affected by loss of precision.

Not having explicit knowledge of the type of some variables therefore makes it harder to rigorously justify a claim that the code works as intended. This means more effort to justify claims of "fit for purpose" in high-criticality (e.g. safety-critical or mission-critical) domains.

The other, more common drawback, is the temptation for a programmer to use auto as a blunt instrument to force code to compile, rather than thinking about what the code is doing, and working to get it right.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • 63
    It is interesting to note that if such examples are the drawback of using `auto`, then most duck-typed language suffer such drawback by design! – Leben Asa Jan 13 '16 at 07:45
  • 14
    If `CallSomeFunction()` returns a different type depending on the sequence of its arguments, that's a design defect of `CallSomeFunction()`, not a problem of `auto`. If you don't read the documentation of a function you are using prior to using it, that's a defect of the programmer, not a problem of `auto`. -- But I understand that you're playing devil's advocate here, it's just that Nir Friedman has the much better case. – DevSolar Jan 13 '16 at 08:51
  • 20
    @DevSolar: Why would `T CallSomeFunction(T, int, int)` be a design defect? Obviously it "returns a different type depending on the sequence of its arguments." – MSalters Jan 13 '16 at 08:59
  • @MSalters: Putting your example together with Peter's example into a beaker and giving it a shake, we end up with an example where we're shifting around three `int`'s. ;-) But I see where you're coming from, and defer to my second point: Don't use a function without having read its documentation. ;-) – DevSolar Jan 13 '16 at 09:04
  • 5
    in most modern IDEs you get info about the variable type when you hover over it, even the auto declared ones – AndersK Jan 13 '16 at 09:04
  • 1
    @Leben - yes, there are drawbacks with using such features in duck-type languages. Such features are not applicable every problem. Yes, ducks walk, swim, and quack. But not everything that does those things is a duck. – Peter Jan 13 '16 at 10:00
  • 1
    @DevSolar - overloaded functions may well have different return types, depending on the type of their arguments. That is not always a flawed design. A common source of bugs (in fact, a lot of beginners questions on SO) come about simply through someone not having read the documentation, even of standard library functions they are using - and `auto` is a feature - like many when misused - that allows a programmer to get flawed code past a compiler. I treat potential of misuse as a drawback. – Peter Jan 13 '16 at 10:10
  • 4
    The most insidious runtime problem would be if a float is substituted with an int, and later code silently loses precision. – sashoalm Jan 13 '16 at 10:47
  • 10
    "The main drawback is that, by using `auto`, you don't necessarily know the type of object being created." Can you elaborate on why this is a problem with `auto`, and not a problem with subexpression temporaries? Why is `auto result = foo();` bad, but `foo().bar()` not? – Angew is no longer proud of SO Jan 13 '16 at 10:56
  • 3
    I would suggest it is ALSO a drawback of subexpression temporaries. – Peter Jan 13 '16 at 12:56
  • 24
    It seems from comments that "drawback" is being interpreted as a reason that something is unacceptable. A drawback of a language feature is a disadvantage that the developer needs to consider and justify accepting or not - i.e. making engineering trade-offs. I am not making blanket claims about why the feature should or should not be used. – Peter Jan 13 '16 at 13:02
  • 7
    @Peter: *The more sinister change is where your change sails past the compiler, but subsequent code behaves in different and unknown - possibly buggy - ways.* If it sailed past the compiler, it means that your code type-checked. If the behaviour of your program changed, it means that the explicit type of your variable was inducing an implicit conversion. Either the conversion should be declared `explicit`, or a subsequent expression is overloaded in a semantically inconsistent way. In short, something's fishy and you'd better find out what. – Laurent LA RIZZA Jan 13 '16 at 13:40
  • Not arguing with that, Laurent. – Peter Jan 13 '16 at 13:53
  • 1
    @Peter: I thought you would not :) This sentence in your answer is actually a fair one, and I agree with it. I advocate to hand-wavily use `auto` everywhere in new code, but to be cautious when changing an existing explicit type declaration to `auto` in legacy code. – Laurent LA RIZZA Jan 14 '16 at 08:41
  • 3
    @sashoalm: that's why the uniform initialization was also introduced. A `int` can't be uniform-initialized from a float. – Laurent LA RIZZA Jan 14 '16 at 08:48
  • 3
    **This.** So much this. People mock me for not being an `auto` fanboy but this is a serious drawback when people write it everywhere without actually thinking. – Lightness Races in Orbit Jan 15 '16 at 10:33
  • @LaurentLARIZZA Technically and perhaps sadly, by some sketchy definition of "can", an `int` can be initialised from a `float`. The Standard only mandates that a diagnostic is issued where narrowing can occur when using brace initialisation. It doesn't necessarily stop compilers from compiling the code. `g++`, for instance, just emits a warning... presumably causing furious debate between those who want a hard error and those who don't understand uniform initialisation and want no warning. [edit] oh yeah: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55783 – underscore_d May 23 '17 at 11:07
  • @underscore_d: Sadly, indeed. At least G++5 produces an error when narrowing a constant, and a warning otherwise. – Laurent LA RIZZA Jun 06 '17 at 11:15
  • What are advantages of 'auto'? I mean, such that are not based on personal taste. – BorisV Mar 16 '22 at 09:54
  • @BorisV - If you want answers on that, I suggest posting it as a full-blown question, rather than (as you've done here) adding a query to comments. That gives a range of people the opportunity to provide answers AND for there to be some feedback on how useful (or otherwise) those answers are. [It also means you get feedback on the question itself - how useful it is, how well constructed, etc]. – Peter Mar 17 '22 at 01:11
82

This isn't a drawback of auto in a principled way exactly, but in practical terms it seems to be an issue for some. Basically, some people either: a) treat auto as a savior for types and shut their brain off when using it, or b) forget that auto always deduces to value types. This causes people to do things like this:

auto x = my_obj.method_that_returns_reference();

Oops, we just deep copied some object. It's often either a bug or a performance fail. Then, you can swing the other way too:

const auto& stuff = *func_that_returns_unique_ptr();

Now you get a dangling reference. These problems aren't caused by auto at all, so I don't consider them legitimate arguments against it. But it does seem like auto makes these issue more common (from my personal experience), for the reasons I listed at the beginning.

I think given time people will adjust, and understand the division of labor: auto deduces the underlying type, but you still want to think about reference-ness and const-ness. But it's taking a bit of time.

Dev Null
  • 4,731
  • 1
  • 30
  • 46
Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
  • Why can you deep-copy an expensive object to begin with? – Laurent LA RIZZA Jan 14 '16 at 09:04
  • 3
    @LaurentLARIZZA: Some classes have copy constructors simply because they are sometimes needed (e.g., instances of `std::vector`). Being expensive to copy is not a property of a class, but of individual objects. So `method_that_returns_reference` might refer to an object of a class that has a copy constructor, but which object happens to be quite expensive to copy (and cannot be moved from). – Marc van Leeuwen Jan 14 '16 at 09:19
  • @MarcvanLeeuwen: If the object is expensive to copy, and cannot be moved from, why would it be stored in an `std::vector`? (Because it might, yes, or because you don't control the class, but that's not the point) If it is expensive to copy, (and owns no resource, because it is copyable), why not use COW on the object? Data locality is already killed by the size of the object. – Laurent LA RIZZA Jan 14 '16 at 10:59
  • 2
    @LaurentLARIZZA Not an instance of something stored in a vector being expensive, just a regular e.g. vector is expensive to copy, it's a heap allocation + O(N) work. Moving is a red herring. The first line I showed will copy, not move, unless the reference returned is an rvalue reference. COW is really neither here nor there. The fact is that expensive to copy objects will always exist. – Nir Friedman Jan 14 '16 at 15:11
  • `operator*()&&` on `unique_ptr` should probably return a `T` not a `T&`. This is a defect in `unique_ptr`, exposed by your example. – Yakk - Adam Nevraumont Aug 10 '16 at 14:06
  • 4
    @Yakk It can't safely do that, because it could slice. The only safe thing it can do is `= delete` that overload. Though more generally what you say is a solution. This is a topic I've explored, if you're interested: http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/. – Nir Friedman Aug 12 '16 at 18:31
  • @NirFriedman Ah, blast, did forget about slicing there. I really want the "the lifetime of the return value of this function depends on the following lifetimes" (the object, the arguments) markup, which would both permit error reporting *and* maybe permit an extension where lifetime extension is transitive. – Yakk - Adam Nevraumont Aug 12 '16 at 20:07
55

Other answers are mentioning drawbacks like "you don't really know what the type of a variable is." I'd say that this is largely related to sloppy naming convention in code. If your interfaces are clearly-named, you shouldn't need to care what the exact type is. Sure, auto result = callSomeFunction(a, b); doesn't tell you much. But auto valid = isValid(xmlFile, schema); tells you enough to use valid without having to care what its exact type is. After all, with just if (callSomeFunction(a, b)), you wouldn't know the type either. The same with any other subexpression temporary objects. So I don't consider this a real drawback of auto.

I'd say its primary drawback is that sometimes, the exact return type is not what you want to work with. In effect, sometimes the actual return type differs from the "logical" return type as an implementation/optimisation detail. Expression templates are a prime example. Let's say we have this:

SomeType operator* (const Matrix &lhs, const Vector &rhs);

Logically, we would expect SomeType to be Vector, and we definitely want to treat it as such in our code. However, it is possible that for optimisation purposes, the algebra library we're using implements expression templates, and the actual return type is this:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Now, the problem is that MultExpression<Matrix, Vector> will in all likelihood store a const Matrix& and const Vector& internally; it expects that it will convert to a Vector before the end of its full-expression. If we have this code, all is well:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

However, if we had used auto here, we could get in trouble:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • This is a legitimate example, don't get me wrong, but expression templates and proxies generally are relatively rare. I think in rather large swathes of code, I've never seen a proxy outside of matrix libraries and the abomination that is vector. So saying that it's the primary drawback is strong. Edit: just to clarify, I'm NOT the downvoter :-). – Nir Friedman Jan 13 '16 at 17:10
  • 3
    @NirFriedman You're right it is strong, but I actually feel that `auto` has very few drawbacks, so I stand by that strength. And other examples of proxies etc. include various "string builders" and similar objects found in DSLs. – Angew is no longer proud of SO Jan 13 '16 at 20:18
  • 2
    I've been bitten by expression templates and `auto` before, specifically with the Eigen library. It's especially tricky because the problem often doesn't show up in debug builds. – Dan Jan 13 '16 at 21:38
  • The problem of not knowing the type can be avoided with Hungarian notation. – QuentinUK Jan 14 '16 at 03:32
  • 1
    Use of `auto` can also bite when using the [Armadillo](http://arma.sourceforge.net/) matrix library, which makes heavy use of template meta-programming for optimization purposes. Fortunately the developers have added the [.eval()](http://arma.sourceforge.net/docs.html#eval_member) function which can be used to avoid the problems with `auto` – mtall Jan 14 '16 at 03:52
  • Note: `auto valid = isValid(...)` is no shorter than `bool valid = isValid(...)`! – user253751 Jan 14 '16 at 22:25
  • The assertion *"you don't really know what the type of a variable is." * is correct. When you wrote the code, you knew (if you wanted to know) what the type was. Now that some time has passed and someone has updated something, you think you know the type or that it is what you remember, but you do not know, because someone might have changed the source's type. I agree with the rest of your post. – Trisped Jan 14 '16 at 23:17
  • @immibis: but does `isValid` really return a `bool`, or is there a conversion going on? – Laurent LA RIZZA Jan 15 '16 at 06:23
  • 3
    _"If your interfaces are clearly-named, you shouldn't need to care what the exact type is"_ Your compiler cannot check the correctness of your code by studying the names of variables. This is the entire point of a type system. Blindly bypassing it is silly! – Lightness Races in Orbit Jan 15 '16 at 10:34
  • @LightnessRacesinOrbit I haven't done any statistics, but I'd say that most expressions in typical code contain several unnamed temporaries (whose type is, by definition, not spelled out explicitly) for each definition of a named variable (whose type *could* be spelled out explicitly). If it's not a problem for the temporaries local to an expression, why is it a problem for the named variables local to a function? – Angew is no longer proud of SO Jan 15 '16 at 10:45
  • 2
    @Angew: It isn't a problem for the temporaries because you're usually immediately using them, which without `auto` generally involves some kind of type check (and splashing `auto` in everywhere takes that type safety away just as it does everywhere else). It's not a good comparison. – Lightness Races in Orbit Jan 15 '16 at 10:51
  • The first paragraph deserves a downvote. The rest is good. Now I don't know how to vote :-| --- On a tangent: You give an excellent example to be really, really weary of expression templates. Whenever you are trying to do complex magic under the hood, making things appear in ways which they are not, the magic is just waiting to explode in your face. You need really good arguments to unleash such dark arts on code that is supposed to be useful. – cmaster - reinstate monica Oct 17 '20 at 11:36
13

It makes your code a little harder, or tedious, to read. Imagine something like that:

auto output = doSomethingWithData(variables);

Now, to figure out the type of output, you'd have to track down signature of doSomethingWithData function.

Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82
Skam
  • 7,298
  • 4
  • 22
  • 31
  • 42
    Not always. `auto it = vec.begin();` is a lot easier to read than `std::vector::iterator it = vec.begin();` for example. – Jonathan Potter Jan 13 '16 at 04:03
  • 4
    Agreed. It depends on the use case. I could have been a more precise about that. – Skam Jan 13 '16 at 04:09
  • 1
    @SeeDart yeah, people who are using auto like that are doing it wrong. – lciamp Jan 13 '16 at 05:54
  • 6
    "Track down the function signature", if that isn'tisn't a mouse hover or a key press away ("follow symbol"/" go to declaration"/whatever it is called), you need to either configure your editor more or switch to an IDE which can do this without configuration... Your point is still valid, though. – hyde Jan 13 '16 at 20:33
  • 7
    I noticed that not in an IDE but in small peepholes of diffs when reviewing checkins! With auto they are harder to read for that reason. – JDługosz Jan 14 '16 at 05:31
  • @lciamp: no, they're doing it right most of the time, because no implicit conversion is involved, and `output` is always the right type to store the return value. Then, if there is a need for a conversion, I'd either specify the exact type, or cast. But I add something to the code only when there is something fishy like a necessary implicit conversion going on. But I agree that it's hard to have a hint to the exact type of the variable with names like `auto`, `output` `doSomethingWithData` and `variables`. – Laurent LA RIZZA Jan 15 '16 at 06:19
  • 3
    If the author of `doSomethingWithData` whould have cared to call his method `crossProductOf(variables)`, the returned type would have been irrelevant. – xtofl Jan 20 '16 at 09:55
13

One of the drawbacks is that sometimes you can't declare const_iterator with auto. You will get ordinary (non const) iterator in this example of code taken from this question:

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");
Community
  • 1
  • 1
ks1322
  • 33,961
  • 14
  • 109
  • 164
  • 3
    Well, you get an `iterator` in any case since your map is non-`const`. if you want to convert it to a `const_iterator`, either specify the variable type explicitly as usual, or extract a method so that your map is const in the context of your `find`. (I'd prefer the latter. SRP.) – Laurent LA RIZZA Jan 14 '16 at 11:47
  • `auto city_it = static_cast(map).find("New York")` ? or, with C++17, `auto city_if = std::as_const(map).find("New York")`. – Dev Null Oct 28 '17 at 05:57
13

Like this developer, I hate auto. Or rather, I hate how people misuse auto.

I'm of the (strong) opinion that auto is for helping you write generic code, not for reducing typing.
C++ is a language whose goal is to let you write robust code, not to minimize development time.
This is fairly obvious from many features of C++, but unfortunately a few of the newer ones like auto that reduce typing mislead people into thinking they should start being lazy with typing.

In pre-auto days, people used typedefs, which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be, which removes one of the most powerful C++ tools from the toolbox and risks breaking their code.

Generally, if you use auto, it should be because your code works for any reasonable type, not because you're just too lazy to write down the type that it should work with. If you use auto as a tool to help laziness, then what happens is that you eventually start introducing subtle bugs in your program, usually caused by implicit conversions that did not happen because you used auto.

Unfortunately, these bugs are difficult to illustrate in a short example here because their brevity makes them less convincing than the actual examples that come up in a user project -- however, they occur easily in template-heavy code that expect certain implicit conversions to take place.

If you want an example, there is one here. A little note, though: before being tempted to jump and criticize the code: keep in mind that many well-known and mature libraries have been developed around such implicit conversions, and they are there because they solve problems that can be difficult if not impossible to solve otherwise. Try to figure out a better solution before criticizing them.

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 3
    `which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be` Not really a good reason IMO. Up-to-date IDE's, Visual Studio 2015 for example, allow you to check the type of the variable by hovering over `auto`. This is _*exactly*_ the same as the `typedef` one. – Hatted Rooster Jan 14 '16 at 15:22
  • @JameyD: You're missing several crucial points there: (1) Your IDE argument only works if the type is concrete, not templated. IDEs cannot possibly tell you the correct type in the case of dependent types, e.g. `typename std::iterator_traits::value_type`. (2) The ***entire point*** was that the inferred type need **not** be "exactly the same" as the correct type intended by the previous designer of the code; by using `auto`, you're taking away the designer's ability to specify the correct type. – user541686 Jan 14 '16 at 15:36
  • You are basically talking about proxies, which one of the answers already mentions. Expression templates and vector nonsense aren't everyday code for most people. In most situations, you *don't* want implicit conversions, and auto helps with that. Herb Sutter talks about the advantages of auto extensively in one of his blog posts, and it's not mostly about keystrokes, and it's also not just for generic code. Also, the first link you provided, the blog post is simply terrible advice (which is why he's criticized vociferously in his comments section). – Nir Friedman Jan 14 '16 at 18:15
  • @NirFriedman: *"...`vector` nonsense"*... pardon? How do you think `bitset` is implemented? Or do you consider bit containers to be nonsense altogether?! – user541686 Jan 15 '16 at 01:18
  • Vector is nonsense, for reasons discussed to death already, and this is largely consensus. Bitsets don't need proxies for anything, you can just use set and test. Operator[] for such containers is syntactic sugar that's not really worth it imho. – Nir Friedman Jan 15 '16 at 03:11
  • @NirFriedman: I like how you dismiss bitset's operator[] with "imho" when it's such a clear counterexample to your shortsighted dismissal of vector problem... – user541686 Jan 15 '16 at 03:30
  • @Mehrdad I'm not dismissing anything, just pointing out that it's syntactic sugar. What's the real value added of using operator[] over set/test? Proxies are problematic; they introduce complexity and they don't play well with type deduction generally. The problems are not specific to auto, they apply to templates as well. Proxies should only be used when they provide a lot of value. Matrix expression templates do. Bitset syntactic sugar doesn't. A discussion of why vector was a bad idea, since apparently its news to you: http://www.gotw.ca/publications/N1185.pdf – Nir Friedman Jan 15 '16 at 04:15
  • 1
    @NirFriedman: Nothing about vector is news to me. What I'm trying to tell you and that you're blatantly refusing to understand is that for the purposes of **this question** bitset is no different from vector -- **both** of them use proxies, because the proxies were deemed to be useful, and the fact that proxies are useful is a reality you need to accept instead of living in denial of. Can you please stop turning this into a debate about whether you think proxies are useful? That's not the subject of debate, and also, your opinion on them is just your opinion, not some kind of fact. – user541686 Jan 15 '16 at 04:24
  • @Mehrdad Glad it's not news to you, here's a cookie for knowing so much! What I'm trying to tell you is: the overwhelming majority of the time, people are not using proxies. People actually actively avoid them in many cases. Anyhow, here's another opinion for you: everything in your answer is already covered by another answer, except better. So maybe you should just upvote Angew's answer if you haven't already, and delete yours? Great talking, have a nice day. – Nir Friedman Jan 15 '16 at 05:00
  • @NirFriedman: My answer explains things differently, and I feel my explanation has value so I don't see why I should delete it. And thanks for the cookie and the downvote out of frustration, you obviously really make really excellent contributions to this site. – user541686 Jan 15 '16 at 05:06
  • Mehrdad, @NirFriedman: I think Nir's argument about what people do usually/mostly is not a very convincing argument... most people most of the time do things you don't see or hear (since they're not in commonly-used libraries); plus, what they do is very much shaped by design decisions made by the standard committee and other library designers. Still, I think Mehrdad's position about the purpose of `auto` is too extreme. Think about what happens when instead of `auto x = foo(); bar(x);` you have `bar(foo());` - here as well you "reduce typing" and that's perfectly legit. – einpoklum Mar 27 '17 at 20:23
  • @einpoklum: Wait, at first I thought I understood it but now I'm confused what part of my position exactly you're arguing against. Can you give a more complete example? What are the definitions of `foo` and `bar` here? (what are you trying to show?) – user541686 Mar 27 '17 at 21:42
6

auto does not have drawbacks per se, and I advocate to (hand-wavily) use it everywhere in new code. It allows your code to consistently type-check, and consistently avoid silent slicing. (If B derives from A and a function returning A suddenly returns B, then auto behaves as expected to store its return value)

Although, pre-C++11 legacy code may rely on implicit conversions induced by the use of explicitly-typed variables. Changing an explicitly-typed variable to auto might change code behaviour, so you'd better be cautious.

Laurent LA RIZZA
  • 2,905
  • 1
  • 23
  • 41
  • Downvoting is fair, but could you please comment on why? – Laurent LA RIZZA Jan 17 '16 at 18:02
  • 1
    I didn't downvote you, but `auto` does have drawbacks per se (or at least - many think it does). Consider the example given in the second question in [this panel discussion](https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Scott-Andrei-and-Herb-Ask-Us-Anything) with Sutter, Alexandrescu and Meyers: If you have `auto x = foo(); if (x) { bar(); } else { baz(); }` and `foo()` returns `bool` - what happens if `foo()` changes to return an enum (three options instead of two)? The `auto` code will continue to work, but produce unexpected results. – einpoklum Mar 27 '17 at 21:49
  • @einpoklum: And does using `bool` instead of `auto` change anything in case of an unscoped enum? I may be wrong (can't check here), but I think the only difference is that conversion to `bool` happens at variable declaration instead of at evaluation of the condition in the `if`. If the `enum` is scoped, then conversion to `bool` shall not happen without an explicit notice. – Laurent LA RIZZA Mar 30 '17 at 07:08
4

Keyword auto simply deduce the type from the return value. Therefore, it is not equivalent with a Python object, e.g.

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

Since auto is deduced during compilation, it won't have any drawback at runtime whatsoever.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Leben Asa
  • 307
  • 1
  • 6
  • 1
    yes, it basically does what `type()` in python does. It deduces the type, it doesn't create a new variable of that type. – lciamp Jan 13 '16 at 05:50
  • 2
    @lciamp Actually, that would be `decltype`. `auto` is for variable assignment specifically. – Cubic Jan 13 '16 at 12:26
4

What no one mentioned here so far, but for itself is worth an answer if you asked me.

Since (even if everyone should be aware that C != C++) code written in C can easily be designed to provide a base for C++ code and therefore be designed without too much effort to be C++ compatible, this could be a requirement for design.

I know about some rules where some well defined constructs from C are invalid for C++ and vice versa. But this would simply result in broken executables and the known UB-clause applies which most times is noticed by strange loopings resulting in crashes or whatever (or even may stay undetected, but that doesn't matter here).

But auto is the first time1 this changes!

Imagine you used auto as storage-class specifier before and transfer the code. It would not even necessarily (depending on the way it was used) "break"; it actually could silently change the behaviour of the program.

That's something one should keep in mind.


1At least the first time I'm aware of.

Dronz
  • 1,970
  • 4
  • 27
  • 50
dhein
  • 6,431
  • 4
  • 42
  • 74
  • 1
    You'd get a compiler error anyway when trying to compile. – Hatted Rooster Jan 13 '16 at 09:41
  • @JameyD: What would do so? why should 2 valid code situations with diferent meaning resutl ever in error? – dhein Jan 13 '16 at 09:45
  • 8
    If you're relying on "no type implies `int`" in C, you deserve all the bad stuff that you'll get from this. And if you're not relying on it, using `auto` as a storage class specifier *alongside a type* will give you a nice compilation error in C++ (which is a Good Thing in this case). – Angew is no longer proud of SO Jan 13 '16 at 10:30
  • 1
    @Angew well thats the case I'm refering to, yeah. **I'm** not doing this. But it is something that should at least be kept in mind. – dhein Jan 13 '16 at 11:10
2

As I described in this answer auto can sometimes result in funky situations you didn't intend. You have to explictly say auto& to have a reference type while doing just auto can create a pointer type. This can result in confusion by omitting the specifier all together, resulting in a copy of the reference instead of an actual reference.

Community
  • 1
  • 1
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • 2
    That's not funky. That's what `auto` does, never inferring a reference nor `const` type. For an `auto` reference, you'd better use `auto&&`. (a universal reference) If the type is not cheap to copy or owns a resource, then the type should not be copyable to begin with. – Laurent LA RIZZA Jan 14 '16 at 09:01
2

One reason that I can think of is that you lose the opportunity to coerce the class that is returned. If your function or method returned a long 64 bit, and you only wanted a 32 unsigned int, then you lose the opportunity to control that.

kblansit
  • 29
  • 2
  • 1
    There's static_cast, and IIRC for example Meyers' Effective Modern C++ even recommends using it to specify type for auto-typed variable. – hyde Jan 13 '16 at 20:23
2

I think auto is good when used in a localized context, where the reader easily & obviously can deduct its type, or well documented with a comment of its type or a name that infer the actual type. Those who don't understand how it works might take it in the wrong ways, like using it instead of template or similar. Here are some good and bad use cases in my opinion.

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Good Uses

Iterators

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Function Pointers

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Bad Uses

Data Flow

auto input = "";

..

auto output = test(input);

Function Signature

auto test (auto a, auto b, auto c)
{
    ..
}

Trivial Cases

for(auto i = 0; i < 100; i++)
{
    ..
}
Khaled.K
  • 5,828
  • 1
  • 33
  • 51
  • 2
    When you want an `int`, you have to type one more char if you go for `auto`. That's unacceptable – Rerito Jan 20 '16 at 10:46
  • @Rerito yes, it's an `int` as easily seen here & typing `int` is shorter. That's why it's a trivial case. – Khaled.K Jan 20 '16 at 10:54
  • Why would you not use "using X = ClassWithLongName1"? – user997112 Sep 25 '20 at 22:46
  • @user997112 you can totally use that, but it also force you to create an alias everywhere you use it & remember it every-time you need it, your comment is more suitable on the question than here.. for my answer, the OP question is about the uses of `auto` – Khaled.K Sep 28 '20 at 11:20
  • @Khaled.K I meant a lot of people argue 'auto' avoids long class names, but 'using' allows this too, without obfuscating and losing control. – user997112 Sep 28 '20 at 23:14
  • @user997112 again, yes you can go with `using`, but if `auto` is used correctly, compiler will always assign the correct data-type, and it saves the programmer the hassle of defining an alias and keep track of it – Khaled.K Sep 29 '20 at 09:01
  • @Khaled.K You honestly think it's hassle writing one 'using' statement? – user997112 Sep 29 '20 at 21:57
2

I'm surprised nobody has mentioned this, but suppose you are calculating the factorial of something:

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

This code will output this:

Factorial of 40 = 0
Size of factorial: 4

That was definetly not the expected result. That happened because auto deduced the type of the variable factorial as int because it was assigned to 1.

xilpex
  • 3,097
  • 2
  • 14
  • 45
1

Another irritating example:

for (auto i = 0; i < s.size(); ++i)

generates a warning (comparison between signed and unsigned integer expressions [-Wsign-compare]), because i is a signed int. To avoid this you need to write e.g.

for (auto i = 0U; i < s.size(); ++i)

or perhaps better:

for (auto i = 0ULL; i < s.size(); ++i)
Paul R
  • 208,748
  • 37
  • 389
  • 560
  • 1
    Yes I find this one irritating too. But the hole in the language is somewhere else. For this code to be truly portable, assuming `size` returns `size_t`, you'd have to be able to have a `size_t` literal like `0z`. But you can declare a UDL to do this. (`size_t operator""_z(...)`) – Laurent LA RIZZA Jan 24 '17 at 08:09
  • 1
    Purely theoretical objections: `unsigned` is quite likely not to be nearly large enough to hold all values of `std::size_t` on mainstream architectures, so in the unlikely event that someone had a container with an absurdly gigantic number of elements, using `unsigned` could cause an infinite loop over the lower range of indices. While this is unlikely to be an issue, `std::size_t` should be used to get clean code that properly signals intent. I'm not sure even `unsigned long long` is strictly guaranteed to suffice, although in practice it presumably must be the same. – underscore_d Jun 06 '17 at 11:55
  • @underscore_d: yes, fair point - `unsigned long long` is guaranteed to be at least 64 bits, but in theory `size_t` could be bigger than this, I suppose. Of course if you have > 2^64 elements in your container then you may have bigger problems to worry about... ;-) – Paul R Jun 06 '17 at 12:08