# With Respect to « Proper Usage » of auto

A French version of what follows is available here. I normally don't write bilingual versions of the articles you'll find on my site, but what follows was written following discussions wiht English-speaking friends and I wanted to make sure they could read it.

What follows expresses my thoughts (for the time being) with respect to thekeyword as it is used to define a variable. These thoughts are incomplete and still evolving, but I hope they will contribute something to the debate.

Please note that thekeyword is used for other reasons than those discussed below, including the types returned by functions and the type of their arguments. I don't have the time I would need to cover these here for the time being.

Thekeyword was traditionally reserved for what we could simplify as « a local variable » (automatic allocation, on the stack, as opposed to static) and was traditionally not used since is was essentially redundant. Since C++ 11, this keyword makes it possible to express, when defining a variable, « the variable's type is the same as the type of the expression used to initialize it » (minus références and const and volatile qualifications).

For example:

static const int glob = 3;
int &f() { return glob; }
int g() { return glob; }
int main() {
int i0 = glob;       // i0 is int and is initialized with a copy of glob's value
auto i1 = glob;      // i1 is int (not const) and is initialized with a copy of glob's value
auto i2 = f();       // i2 is int (not int&!) and is initialized with a copy of glob's value
auto i3 = 0.5 + g(); // i3 is double since expression 0.5 + g() is double
}

Quite a few experts are debating the merits of usingto « specify » a variable's type There are at least three groups involved:

## The AAA Position

The AAA position recommends that we useto define a variable almost as a reflex. This position states (rightfully) that the resulting text is homogeneous and prevents such things as forgetting to initialize a variable.

Without auto With Notes
int i = 0;
auto i = 0;
// or
auto i = int{};
// or
auto i = int();
// or
auto i = int{0};

Like most people, I would not usehere, but a supporter of AAA would remind us that this:

auto i = int();

... compiles and initializes i to zero, whereas this:

int i();

... compiles but could surprise by declaring a function named i that takes no argument and returns an int.

std::vector<std::string> v = { "I love", "my", "teacher" };
for (std::vector<std::string>::const_iterator it = v.cbegin(); it != v.cend(); ++it){
std::cout << *it << ' ';
}
// or
for (const std::string &s : v) {
std::cout << s << ' ';
}
std::vector<std::string> v = { "I love", "my", "teacher" };
for (auto &it = v.cbegin(); it != v.cend(); ++it){
std::cout << *it << ' ';
}
// or
for (const auto &s : v) {
std::cout << s << ' ';
}

Here, for the first loop, it would feel natural to usefor it as well as begin(v) and end(v) for the extremities of the sequence we want to iterate on. This would make of it, contextually, a vector<string>::iterator or a vector<string>::const_iterator depending on the type of v.

I used v.cbegin() and v.cend() to make examples similar to one another, nothing more.

tetemplate <class It>
void f(It begin, It end) {
typename std::iterator_traits<It>::difference_type dist = std::distance(begin, end);
// ...
}
template <class It>
void f(It begin, It end) {
auto dist = std::distance(begin, end);
// ...
}

This example shows, I think, how useful the keyword can be.

We could go for a very explicit type declaration, such as shown in the left example, but it's unclear how it would be advantageous. In such a case, I doubt most programmers would really benefit from explicitly stating the type; should one not be conscious of the fact that the return type of std::distance() should be signed, explicitly stating the time would probably not provide more information.

template <class M, class F, class ... Args>
auto minuter(M minu, F f, Args && ... args) -> std::pair<decltype(f(std::forward<Args>(args)...)), typename M::duration> {
typename M::time_point before = minu.now();
decltype(f(std::forward<Args>(args)...)) result = f(std::forward<Args>(args)...);
typename M::time_point after = minu.now();
return std::make_pair(resultat, after - before);
};
template <class M, class F, class ... Args>
auto minuter(M minu, F f, Args && ... args) {
auto before = minu.now();
decltype(auto) result = f(std::forward<Args>(args)...);
auto after = minu.now();
return std::make_pair(resultat, after - before);
};

In this case, using auto frees us from having to know the names of types exposed the the language's standard clocks, in such a way that we can express the same algorithm without loss. It also frees us from repeating ourselves, thus reducing the risk of error.

There is a subtelty associated with the type of result in the right-hand example. Here, simply usingwould have probably worked, in most cases, but would have induced a form of semantic drift... A sign, I think, that one should not stop thinking, even when using something such as.

Usingto define a variable turns uninitialized variables into illegal expressions. Indeed, this would not compile:

auto x;

... whereas this would compile but leave x in an uninitialized state:

int x; // legal, but...

Aesthetically speaking, an upside of usingis that it aligns variable names as they are being defined in a given block, all « type names » being of the same length.

## The AAAA Position

The AAAA position is that usingwhen defining a variable distances source code from its semantics, which can lead to errors and complicate maintenance.

With Possible intention In practice
auto s = "I love my teacher";

The programmer might have wanted to make s of type std::string.

Here, s is a const char*. We could have made it a std::string by using a more explicit right-hand side, such as:

auto s = string{"I love my teacher"};

... or, since C++ 14, with a user-defined literal:

auto s = "I love my teacher"s;
auto x = d + sizeof(T);

The programmer might have wanted to compute the size required in order to store a T and something that is d bytes-wide.

The type of x is a mystery: is it signed or not? Integral or not? Could the value of d be negative? This example comes from James McNellis, who claims (rightfully) that usinghere is a really good way to get oneself into trouble.

std::vector<std::string> v = { "I love", "my", "teacher" };
for (auto s : v) {
std::cout << s << ' ';
}

The programmer will iterate over each string in v but, in so doing, will create a potentially costly copy of each element.

Writing either for(auto &s:v) or for(const auto &s:v) would solve this one.

With a loop that makes it explicit that each s is a string, explicitly writing string& or const string&, would have also done the job.

One could plead that, which might seem magic in the eyes of some, obscures slightly this mostly hygienic issue.

template <class M, class F, class ... Args>
auto minuter(M minu, F f, Args && ... args) {
auto avant = minu.now();
auto result = f(std::forward<Args>(args)...);
auto apres = minu.now();
return std::make_pair(resultat, apres - avant);
};

The value returned by f(args...) will be copied in result, but this could break the expected semantics should this function call return a reference.

As stated above, decltype(auto) is the à lasolution with C++ 14 here, and explicitly stating the return type with a complex decltype(...) expression would also work.

Clearly, usingshould not lead us to stop thinking when we are coding.

## The CRA Position

What follows supposes that one writes short functions with a clear focus; if that's not part of your programming practice, whatever the reason, then the AAAA position is probably better suited for you.

Essentially, why do we use when we do so?

On the teaching front, the virtues ofare many: simpler indentation when defining variables, simple directives that work well in general for beginners, reduced risk of uninitialized variables, etc.

In practice,is not magical, and using it without thinking can lead to unpleasant side-effets, in particular when manipulating integers (issues of size and signedness). Of course, coding without thinking is a questionable practice, with or without auto.

Usingis a good idea, in my mind, when:

• The actual type is clear from the immediate context, but would be painful to express explicitly, or
• The exact type is not important, as long as it meets the local algorithmic constraints

In a situation such as the following:

template <class It>   void f(It begin, It end) {      auto dist = std::distance(begin, end);      // ...   }

... I'm not convinced that writinginstead of typename std::iterator_traits<It>::difference_type has a detrimental impact on readability or on readability. Indeed, I think that:

• What interests programmers in this case is that dist represents the number of elements in the sequence
• If a programmer is not aware that dist is a signed integral type in this case, I doubt explicitly expressing its type will add this awareness
• Algorithmically speaking, if function f() needs the type of dist elsewhere, for example in order to declare another variable of the same type, then decltype(dist) will probably be the best option in this situation

Likewise, take this function:

template <class C>
void afficher_elements(const C &c, std::ostream &os) {
for (auto &val : c)
os << val << ' ';
}

In this case, supposing that c is a container, it's possible to express the exact type of each element explicitly:

template <class C>
void afficher_elements(const C &c, std::ostream &os) {
for (typename C::const_iterator it = c.begin(); it != c.end(); ++it)
os << *it << ' ';
}

That being said, what would we gain from this explicit typing? Is it clearer? Easier to maintain?

Then, of course, abusive usage ofdoes exist. The poster child probably is:

auto n = 0;

... where usingtakes more characters than using int explicitly, and runs the risk of confounding those who will read the code a posteriori. Likewise, this:

template <class T, class F>
void g(T val, F f) {
auto x = f(val);
// ... lots of code
}

... can be tricky. What's the intent behind the application of f to val? What does g() expect of x in practice? Here, constraining the type of x would at the same time be a form of in-code documentation and a guide to its ulterior maintenance.

## My Personal Usage of auto

I personally writewhen:

• The type is clear from the context in which it is used, and does not seem to induce confusion
• There's an actual gain in writing; for example, it oftens seems like a win for me to replace witha type that's very long to express, or that depends on a function's arguments without really varying semantically with respect to the algorithm

I avoidwhen:

• Not explicitly stating the type appears to me as obscuring the purpose of the algorithm or complicating code maintenance
• I am of the opinion that the code would benefit from explictly stating the constraints its type expresses

Whenever it's reasonable to express type constraints, we have many options (not necessarily in order of preference, such an order being somewhat cultural):

What about the AAA approach, which recommends almost always using? In some projects, maybe, and it seems reasonable for teaching some introductory programming classes.

What about the AAAA approach, which recommends avoidingexcept in select cases? I would not go as far, the cases of meaningfulseem too numerous to me to impose such a restriction.

Is the type clear from context? Then,is probably appropriate.

Is the exact type irrelevant in the context of the algorithm? Then,is probably appropriate.

## Some Thoughts...

A direct consequence ofusage is, it seems to me, a greater emphasis on explicitly type literals, inclusing user-defined literals. I'm not sure what to think of this yet, but it is a twist given existing practice, where we had a tendency to let constructors perform conversions for us given literals of various types. This was something one could observe already through usage of such functions as, as the return type of that function is determined by the type of one of its arguments.

Acceptance ofis in large part driven by our habits in generic programming. We have come to accept using very abstract types in our code, which are only determined at point of usage, typically far from the location where the templates are actually expressed. The tools we have built (traits in particular) to constrain types and precise our intentions remain valid, but are sometimes a burden. I do not thinkreplaces them completely, even though it plays a rule in alleviating some of their tediousness.

I expect concepts, as they become available, to replaceusage in some cases. They are not as tedious to write as things such as traits on dependent types, and constrain generic code just enough to make the intent clear and code more robust.

In a presentation on migrating code from C++ 03 to C++ 14, Joel Falcou examinesfrom a compile-time-shortening perspective, and offers the following guidelines :

• Follow the AAA philosophy (he adds « in a viral way ») to ensure compile-time performance
• More specifically, he recommends usingall the time, except :
• in non-template, publicreturn types
• when a concrete type is explicitly needed for external reasons

He adds thatshould be used for metaprogramming tasks andcontext inreturn types. These, in his experience, bring the best compile-time performance results, as long as one remains conscious of the caveats ofdeduction rules, discussed at length in Scott Meyers' Effective Modern C++ book.