Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
95 lines (75 sloc) 12.2 KB
---
layout: default
title: Don't use <code>auto</code> unless you mean it
description: "A unified, forward-thinking convention for using the <code>auto</code> keyword in C++: <code>auto</code> means anything. Learn when you should and should not use it."
tag: C++
---
<p>Since it's repurposing in C++11, <a href="http://en.cppreference.com/w/cpp/language/auto">the <code>auto</code> keyword</a> has largely been used as a convenience. It's one of the more well-known new features, perhaps because many programmers saw it as a way to simplify their code, but also has little consensus on when it should be used. It provides automatic type deduction in declarations based on their initializers. For example, <code>auto x = 5;</code> will deduce that <code>x</code> is an <code>int</code>.</p>
<p>To beginner C++ programmers, <code>auto</code> may look like it provides dynamic typing as in JavaScript or Python. This is not the case. The type is still completely static because the type of the initializer is static too. Even if it were a declaration inside a function template, in which the initializer could have a different type depending on the particular template arguments, the type is static for each template instantiation and those template instantiations are themselves static. C++ is still a statically typed language.</p>
<p>For many, <code>auto</code> is seen as a way to increase the brevity of code and hide away ugly C-style declarations, with the intention of making it easier to read and faster to write. After all, <code>auto</code> can save us from typing out the hideously long types we often find in C++. Some suggest to use <code>auto</code> as much as possible. I strongly feel that hiding away types is a stylistic choice and therefore the responsibility of the IDE and not of type deduction. Also, this is a difficult convention to stick to because it's hard to agree on whether a particular use is acceptable or not. Consider the following:</p>
{% highlight cpp %}
// Assuming range-based std::max_element
auto best_candidate = std::max_element(employees, LessSuitableForTask{task});
{% endhighlight %}
<p>Is it clear what we can do with <code>best_candidate</code>? Is it the maximum value or an iterator pointing at the maximum value? If it's an iterator, what kind of iterator? For somebody who is fairly comfortable with C++, perhaps this might be obvious. For anybody else, we'd have to look up the definition of <code>std::max_element</code> just to find out what its return type is and then figure out what that type represents. This convention is therefore a matter of deciding how proficient a person needs to be to understand your code. If the initializer used functions and types from some other library, the reader has to be proficient with those also. When using <code>auto</code>, you are arbitrarily making the choice of whether it is acceptable or not. Additionally, if the initializer changes to give a type that is no longer suitable, you'll get errors from further down the code. This doesn't sound great to me.</p>
<p>Instead of using <code>auto</code> so much, my proposed convention is as follows: <i>choose types that impose the minimum requirements on a declaration that are necessary for the code in which it is used to behave correctly</i>. Under this convention, <code>auto</code> imposes no requirements. It says “This variable can have any type and the code in which it is used will run just fine regardless.” This gives <code>auto</code> some meaningful semantics. You may correctly observe that this means we have only two choices: no requirements with <code>auto,</code> or no leniency with the actual type itself. That is, for now.</p>
<p>A proposal to introduce concepts to C++ is in the works (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4040.pdf">N4040</a> is the latest draft at the time of writing). Concepts would allow us to place requirements (called constraints) on template arguments. At the moment, a template argument (like <code>typename T</code>) will accept any type, which results in horrible errors being thrown from the internals of the template if that type is not appropriate. Go ahead; try doing something like <code>std::copy(5, 10, “foo”)</code> and enjoy the mess. Worse, it could compile fine and behave erratically. With concepts we'll be able to specify requirements on the arguments:</p>
{% highlight cpp %}
template <InputIterator InputIt, OutputIterator OutputIt>
OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
{% endhighlight %}
<p>In fact, a terse syntax is also proposed which will allow us to use concept names in the argument declarations themselves:</p>
{% highlight cpp %}
auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
-> OutputIterator;
{% endhighlight %}
<p>So with concepts, saying that a parameter is an <a href="http://en.cppreference.com/w/cpp/concept/InputIterator"><code>InputIterator</code></a> says that the arguments should meet the requirements imposed by the <code>InputIterator</code> concept. If you pass something that doesn't, you'll get a nice clean error message.</p>
<p>We can also use <code>auto</code> in this terse syntax as the equivalent of <code>typename T</code> with no constraints:</p>
{% highlight cpp %}
void foo(auto something);
{% endhighlight %}
<p>Given the semantics of concepts for arguments, this means “I impose no requirements on the type of <code>something</code>.” Anything will do. This syntax is also being introduced for generic lambdas in C++14:</p>
{% highlight cpp %}
auto add = [](auto x, auto y) {return x + y;};
{% endhighlight %}
<p>It's intended that concepts will eventually be usable in normal variable declarations too, although a proposal has not yet been written. The previous <code>std::max_element</code> example could then be written:</p>
{% highlight cpp %}
InputIterator best_candidate = std::max_element(employees, LessSuitableForTask{task});
{% endhighlight %}
<p>This implies that any use of <code>best_candidate</code> only requires that it meets the requirements of an <code>InputIterator</code>. We are hiding away the actual type of the iterator, but not hiding the information that is actually important. On the other hand, <code>auto</code> hides everything. You can hopefully now see that the convention I have proposed unifies the declarations of both function arguments and variables:</p>
{% highlight cpp %}
some_type name = /* must be convertible to some_type */;
SomeConcept name = /* must meet SomeConcept requirements */;
MoreGeneralConcept name = /* must meet MoreGeneralConcept requirements */;
auto name = /* can be anything */;
void foo(some_type); // must take something convertible to some_type
void foo(SomeConcept); // must take something that meets SomeConcept requirements
void foo(MoreGeneralConcept); // must take something that meets MoreGeneralConcept requirements
void foo(auto); // can take anything
{% endhighlight %}
<p>In both cases, you give the type or concept that has the least requirements required in order to use that object in the way you want to. Under this rule, <code>auto</code> means anything. Likewise, <code>auto&</code> means an lvalue reference to anything, <code>auto&&</code> is a <a href="http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers">universal reference</a>, and so on.</p>
<p>This convention has multiple additional benefits. In the same way that template arguments allow the arguments to a function to change type, using concepts and <code>auto</code> allows the initializer of a declaration to change. As long as the initializer returns an object that meets the requirements, everything is fine. It also ensures that a nice clean error will be reported if a type is used that doesn't meet the requirements, unlike using <code>auto</code> which has the same problems as templates do today. It also makes the code easier to read than if <code>auto</code> were used for everything because you only need to understand what the type or concept represents. Also, concept names are brief and to the point.</p>
<p>If you see <code>auto</code> under this convention, it means you really, really do not need to care about the variable's type. This means it should be used significantly less than it is being used now. For example, I would not use it in a range-based <code>for</code> loop unless I could really be iterating over anything. Instead, I'd use the type itself:</p>
{% highlight cpp %}
for (contact_details contact : contacts) {
send_email(contact.get_email_address(), "Hello, World!");
}
{% endhighlight %}
<p>In this example, I'm calling <code>contact_details::get_email_address</code>, which means that <code>contact</code> has to be a <code>contact_details</code> object. This has a number of advantages over using <code>auto</code>:</p>
<ol>
<li>Finding the type of <code>contact</code> only requires looking at its declaration and not at the type of <code>contacts</code>.</li>
<li>If the container type changes, you'll get an error directly from the <code>for</code> loop declaration, rather than deeper in your code.</li>
<li>It expresses your intent to use <code>contact</code> in ways only a <code>contact_details</code> object could be used.</li>
<li>It's consistent with all other declarations including parameters of templates.</li>
</ol>
<p>I'd say it's also better than writing <code>decltype(v)::value_type</code> because that ties the declaration to the initializer by assuming that it has a <code>value_type</code> typedef. Really, <code>contact</code> doesn't need to care.</p>
<p>If I were writing a generic loop that could iterate over any kind of object and process it, <code>auto</code> would be fine:</p>
{% highlight cpp %}
for (auto element : range) {
process(element);
}
{% endhighlight %}
<p>Since concepts won't be around until C++17 and the ability to use concepts in normal variable declarations may not appear until later, this convention requires a lot of patience. Just don't get too excited about <code>auto</code> and use it everywhere. Instead, use it with some idea of how it will be used in the future. For now, use specific types in most cases. It's better to be overly specific than overly general, because using <code>auto</code> unnecessarily can lead to unclear code and confusing bugs. Using it too much now will make your code less idiomatic in the future. The only downside will be that if you change the type of the initializer, you'll have to change the type of the declaration, but that shouldn't happen too often. Obviously for now we need to be overly general with templates, otherwise they'd be useless, but for declarations we should prefer to be specific.</p>
<p>Herb Sutter has previously proposed <a href="http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/">AAA Style</a> (Almost Always Auto), which is pretty much the opposite of what I'm suggesting. However, we don't entirely disagree. Many of his arguments actually back up what I'm suggesting, particularly in terms of writing against interfaces instead of implementations. In his <code>append_unique</code> example, he remarks that <code>Container& c</code> makes it clear what the requirements of <code>c</code> are. This is simply mimicking what concepts will provide and is certainly a good practice for template arguments while we don't have them. However, <code>auto</code> doesn't have this benefit at all. Instead, <code>auto</code> is like writing <code>typename T</code> without any information about requirements. It certainly is less readable, perhaps not for the writer of the code, but for anybody else who wants to read and understand it.</p>
<p>One problem that the AAA Style tries to solve, which I do not, is the horrible C declaration grammar. By using <code>auto</code> everywhere, we can easily see that a line is a variable declaration and we don't have to decode its type. Firstly, I don't think this should be the responsibility of <code>auto</code>, which is a much more powerful tool than a <code>var</code>-like keyword. Secondly, we can generally avoid the incredibly ugly type names and concepts will help us to do this.</p>
<p>In summary, <code>auto</code> means anything. Only use it when you mean it, otherwise you make your code harder to read and introduce further problems. Just be patient for concepts, which will make this convention unified and logical. It'll pay off.</p>