Home C++11 auto variables
Post
Cancel

C++11 auto variables

The C++11 standard introduced the auto keyword for type deduction. One of its main uses is to make variable declarations more concise. It can also improve the maintainability of code if used correctly as it can reduce redundancy. This post will give an overview of what it does and how to use it.

Side note: Technically, C++11 repurposed the auto keyword from C, but we won’t cover that here.

A quick look

When you create and initialise a variable or object, you normally specify its type and name, and possibly some initialisation value or parameters. The auto keyword replaces some or all of the type declaration. For example, let’s say we’re copying a hypothetical Widget object like this:

1
Widget foo = blah;

The compiler already knows that blah is an instance of Widget so telling it that foo is also a Widget is a little redundant. With the auto keyword, the statement can be rewritten like this:

1
auto foo = blah;

This tells the compiler that foo is going to be the same basic type as blah. It’s important to realise that the actual compiled code underneath is exactly the same as the previous statement. The foo variable is still strongly-typed as a Widget. You can’t change its type later by assigning something else to it.

That’s the basic gist of auto in variable declarations. It’s as simple as that. You can use it for all kinds of local/global initialisations, whether you’re copying an object or a fundamental type directly, storing a literal, or storing the return value of a function.

More realistic examples

The quick look above was deliberately very simple. Let’s look at some more realistic uses of the auto keyword.

Iterators

Let’s say you have a list of Widget objects and you want to iterate over them. Here’s what the conventional code to get the first iterator might look like:

1
std::list<Widget>::iterator iter = widgets.begin();

The type declaration is starting to become quite cumbersome. If we used the auto keyword instead, it would look like this:

1
auto iter = widgets.begin();

The compiler can safely look at the declaration of the begin() method and determine that it returns an iterator into a list of Widget objects. As you can see, the code looks cleaner. If you really need to know the exact type of iter then modern IDEs will often show you if you just hover the mouse over the variable name.

Also, if you were to change the container type (e.g. to use a vector instead of a list) then the auto declaration would still work without any modification. That can make some maintenance and refactoring tasks easier and more robust.

Smart pointers

One of the other great parts of C++11 is the robust smart pointers. Unfortunately, the declarations take up quite a few extra characters. Let’s say you’re making an instance of a Widget object and storing it using a shared pointer. Here’s the non-auto version:

1
std::shared_ptr<Widget> p = std::make_shared<Widget>();

We can simply drop-in the auto keyword to replace the type declaration of pointer variable p:

1
auto p = std::make_shared<Widget>();

In case you’re not familiar with it, make_shared<>() is the safe way to instantiate an object and immediately put it in a shared_ptr. You should not usually do it like this:

1
auto p = std::shared_ptr<Widget>(new Widget); // not safe!

Lambdas

My favourite part of C++11 is lambdas. These are locally-defined function objects which you can store and pass around. Normally, storing them requires the use of std::function which can be slightly unwieldy. This example stores a simple lamdba which just multiplies a number by 2:

1
std::function<int(int)> func = [](int n) { return n * 2; };

For local storage of lambdas (especially complicated ones), auto declarations make life considerably easier:

1
auto func = [](int n) { return n * 2; };

In case you’re wondering about the lambda’s return type, the compiler infers it automatically if the body of the lambda contains nothing but a return statement.

const

If you want your auto declaration to be const, then you’ll normally have to specify that explicitly. Let’s say you’ve got a map of Widget objects and you’re trying to find one by its ID number. You might want to make the returned iterator const if it’s not going to be moved later, e.g.:

1
const std::map<int, Widget>::iterator iter = widgetMap.find(123);

To get the same effect with auto, you need to keep the const keyword where it usually is:

1
const auto iter = widgetMap.find(123);

This is a good example of how the auto keyword can make your code simpler. With auto, you don’t need to worry about the exact template arguments for the iterator declaration because the compiler can infer them directly from the find() method.

References

You’ll have to add the ampersand (&) explicitly if you want your auto declaration to be a reference. For example, let’s say you have a vector of Widget objects and you want to get a reference to a specific element. Here’s what the conventional code might look like:

1
Widget &w = vec[17];

Here’s the auto version:

1
auto &w = vec[17];

If you leave out the ampersand, you’ll be copying the Widget instance instead of getting a reference to it.

You can explicitly specify const as well if you want to. However, note that if the right-hand side of the assignment is already const then your auto reference will be implicitly const as well. For example:

1
2
const int num = 10;
auto & ref = num; // ref is a const int &

Where not to use auto

Members and parameters

In C++11, the auto keyword only works for local and global variables. That means you cannot use auto to declare a class member or a function parameter. This is because the declaration of a structure or function must be explicit and unambiguous.

If you want something similar to auto behaviour for member variables and function parameters then you might find templates useful.

C++14 adds the ability to declare auto parameters in lambdas.

Implicit up-casts

Despite being generally considered a strongly-typed language, C++ is a little lax about some conversions. For example, you can implicitly up-cast a pointer from a derived class to a publicly-inherited base class:

1
ParentClass * ptr = new ChildClass;

If you use auto carelessly here then you actually change the meaning of the code:

1
auto ptr = new ChildClass;

ptr is now a pointer to a ChildClass object. Depending on your class design, this may or may not be a problem. In many cases, it will be completely harmless. However, it’s important to be aware of implicit conversions that you may be relying on before using auto. An explicit declaration would probably be most appropriate for cases like this, but you could also cast the right-hand side of the assignment.

Implicit literal conversions

This is a similar issue to implicit up-casts, and it particularly affects integer types due to type promotion. The following example stores an integer literal as an unsigned short (typically a 2 byte type):

1
unsigned short num = 123;

If we use auto here, we’ll again have unintended behaviour:

1
auto num = 123;

The auto declaration will resolve to signed int (typically a 4 byte type) because that’s how the compiler will usually interpret an integer literal. Once again, an explicit declaration is typically the best way to go here to avoid the ambiguity.

Common concerns

Strong vs. weak types

Some people might look at auto variables and think C++ is turning into a weakly-typed language. That’s absolutely not the case because the type behaviour is completely unmodified. A variable declared as auto simply has its type deduced (primarily) by the right-hand side of the initialisation statement instead of the left-hand side. That’s all there is to it. It’s still as strongly-typed and as type-safe as ever.

Performance

Another common concern is that auto variables will somehow affect the performance of the program. As with strong vs. weak types this is a non-issue because the type behaviour is completely unmodified. The only difference with auto is where the compiler looks to determine the declaration type. The run-time behaviour and performance should be completely unaffected (unless you’re careless about how you introduce auto into your code).

Conclusion

The auto keyword is a very handy little addition to the arsenal of day-to-day programming tools. I’ve only covered one aspect of its use here, but it’s the aspect which most programmers will probably see and use most often.

It’s vital to remember though that it doesn’t change the underlying code at all. It’s simply a bit of ‘syntactic sugar’ to make your life a little easier.

This post is licensed under CC BY 4.0 by the author.