Lesson #1: ‘auto’

The 'auto' keyword adds type-inference to C++, such that instead of explicitly defining the type when declaring for a variable, you rather simply assign it a value/expression, and let the compiler infer the type based on what is assigned.

As such, it's great at reducing verbosity, and it's a good feature to start this blog with, being used by many of the other new C++11 features we'll be covering later, such as the ranged-for-loop and lambdas. It's also well supported in all the main C++ compilers already – clang (2.9+above), gcc (4.4+above), MSVC (since VC10).

To understand how it works, take a look at the following example of some 'old C++' code, together the new C++11 'auto' using equivalent:

Old C++:

int i = 1;
FILE* fp = fopen("file.txt", "r");
std::map<std::string,std::vector<int> > myMap = fetchMyMap();
std::map<std::string,std::vector<int> >::iterator it = myMap.find("hello world");
for(std::map<std::string,std::vector<int> >::iterator it = myMap.begin(); it != myMap.end(); ++it) {
   const std::vector<int>& v = it.second();
   ...
}

C++11:

auto i = 1;
auto* fp = fopen("file.txt", "r");  // the * here is optional
auto* myMap = fetchMyMap();         // the * here is optional
auto it = myMap.find("hello world");
for(auto it = myMap.begin(); it != myMap.end(); ++it) {
   const auto& v = it.second();
   ...
}

More than just Syntactical Sugar

While auto at first may appear to be little more than trivial syntax sugar, especially if just considering examples like the first two examples above, it becomes more than just syntax sugar in some cases like the map and map::iterator examples above, i.e. particularly when:

  • the typenames are very long, such as when you start using C++ STL collections and their iterators, or other complex template classes (and when the repeating of these long names seems unneccessary and repetitive)
  • the type is really not clear, such as when using lambdas in C++11, i.e. auto x = [](int i) { return i > 0; }; – here for example most programmers would have no idea what this exact type is, and rightly shouldn't have to care what the type is. Sometimes the exact type that is returned is a complicated expression that is really an implementation detail of the library being used, rather than something that the user should have to know and type out (as long as the behaviour of that type – how they should make use of it – is understood).

Still statically typed

The beauty of auto is that it still retains the strong type-safety of C++ – all of these auto variables are still just as strongly typed, the compiler will warn like usual at compile time if they are incorrectly used as another type, there is zero performance impact as the code compiles to exactly the same thing as if the type had been specified, and the auto-complete in the IDE will know what their type is.

Thus, in using auto, you don't sacrifice the any of C++'s performance as compared to dynamically-typed languages, and yet you still gain the reduction in verbosity from not having to repeat typenames everywhere that was previously generally limited to such dynamically-typed languages (although you still have the option of specifying the type when preferred).

When to use 'auto'?

So when should one choose to use auto, and when not?

To start with, one limitation to note is that you can't use auto on class member variables.

Aside from this limitation, Herb Sutter recommends using auto wherever possible – "If you want to explicitly enforce a type conversion, that’s okay; state the target type. The vast majority of the time, however, just use auto".

I would recommend the same – prefer to use auto most of the time – with the following caveat:

  • only choose to use it when variables are being assigned the return value of some function or another variable (such as in most of the examples that were shown above), as there is little gain in using it when simply initializing an object with some variables passed to its constructor, i.e. in converting something like string s("hello") to auto s = string("hello").
  • similarly, for simple built-in types like bool, int, float, double etc, using it in a case like:

    auto i = 3;
    

    is very optional, as it serves little purpose, however if say someFunction() returns some form of integer, then using:

    auto i = someFunction();
    

    can be helpful, i.e. in avoiding issues like having to match the exact integer type being returned by the function.

Also, as I will post about later, modern C++11 code-style is evolving to prefer initializing variables with the results of functions in some cases, where in old C++ one would have initialized them by calling their constructor, thus making the use of auto in such cases make more sense. i.e. instead of doing:

std::shared_ptr<std::string> str(new std::string("hello world"));

the new modern C++11 style, which also happens to be both more concise (removing the need to write the pointed-to-type twice), more efficient (requiring only one memory allocation instead of two), and which eliminates the use of the new operator entirely (removing new + delete usage is another part of modern C++11 style) is to prefer:

auto str = make_shared<std::string>("hello world");

This style, and the C++11 features which support this, such as the above shared_ptr + make_shared, will be covered in later posts.

Which form to use: auto, auto*, auto&, const auto&, …?

Like many things in C++, nothing is ever quite as simple as it first appears.

When I started using auto in my projects, I realized I was still unclear as to whether you have to specify whether the auto variable is a pointer / reference / const, something most introductions to this feature fail to explain. Looking at the specification clarified things:

Generally the logic is:

  • you may use auto& + const auto to force the variable to be a reference + const respectively (though obviously only doing so when it makes sense – you usually don't want to assign a temporary to a reference for example).
  • you may use auto* when the expression being assigned is actually a pointer, however you can't use auto* to force it to be a pointer like with auto& + const auto, just like you couldn't say int* x = some_int;
  • just using auto always works – if you just declare a plain auto variable, then it will automatically be a pointer / reference / const whenever the thing being assigned is.
    • thus, when you're not wanting to force the auto variable to & / const, the choice of whether to specify * / & / const is similar to the choice of whether to specify the actual type – it's somewhat down to personal preference.

I haven't touched on the logic for using auto with C++11's new rvalue references (&&) here, as these will be introduced in a later post, but the same general logic applies.

Some examples to make these rules clear ….

const auto

The auto variable will be const if the thing being assigned to it is const, however you can also specify const auto to force it to be const.

const int fn1();
int fn2();

auto a = fn1();       // a is const
const auto b = fn1(); // b is const (the const here is optional but redundant)
auto c = fn2();       // c is not const
const auto d = fn2(); // d is const

auto&

The same logic applies as with const – if the thing being assigned to the auto variable is already a reference, then the auto variable will be a reference, so explicitly adding an & after auto is purely optional. However you can also specify auto& to force the variable to be a reference (with the usual caution around not assigning a reference to a temporary).

int& fn1();
int fn2();

auto  a = fn1(); // a is a reference
auto& b = fn1(); // b is a reference (the & here is optional but redundant)
auto  c = fn2(); // c is a value
auto& d = fn2(); // compile error - we cannot bind a non-value to a non-const reference
const auto& e = fn2(); // but apparently this is ok, don't ask me why or when one would use it...??

auto*

Like with const and &, if the thing being assigned to the auto variable is a pointer, the variable will be a pointer. However trying to assign a non-pointer to an auto* variable will rightly result in a compile error.

int* fn1();
int fn2();

auto  a = fn1(); // a is a pointer
auto* b = fn1(); // b is a pointer (the * here is optional redundant)
auto  c = fn2(); // c is a value
auto* d = fn2(); // compile error

2 comments

  1. Alexander · · Reply

    Hi, Mike. I just want to point out that your entire post is riddled with errors (At least in VS C++ 2012, and I hope MS somewhat follows ISO std).

    Auto drops “const”, “volatile”, and references(“&”). But not pointers.

    auto a = fn1(); // “a” is “const” . Mistake, “a” is “int” and not “const int”
    And so on ……

    http://msdn.microsoft.com/en-us/library/vstudio/dd293667.aspx
    Has excellent explanation. (I was looking for some info about “vectors” and “unique_ptr”, and found your blog).

    1. ablepharus · · Reply

      I did not find anything about “volatile”, but Stroustrups “The C++ programming language” §6.3.6.1 confirms, that the references and “const” will be dropped.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: