C++ Auto - Automatic Type Deduction

Introducing the auto keyword, which will ask the compiler to automatically deduce our data types
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

3D art showing a fantasy maid character
Ryan McCombe
Ryan McCombe
Posted

C++ compilers can often automatically infer the type of the variables we create. We can ask the compiler to do this using the auto keyword:

// This will create an integer
auto MyNumber { 5 };

Most editors, including Visual Studio, will let us inspect what the type was deduced to be by hovering over the variable name.

Our Number variable predictably has a type of int, which we can confirm by hovering over the variable in our editor:

A screenshot from Visual Studio showing an auto variable being deduced as an integer

Types can also automatically be deduced in a variety of contexts. For example, when initialising a variable, its type can also be deduced from the return type of the function.

float GetFloat() {
    return 3.14f;
}

// Will be float
auto Number { GetFloat() };

auto with C++ Functions

Auto can also be used to automatically determine the return type of the function. This can be inferred by how the function uses the return statement.

Below, GetDouble will have its return type correctly deduced to double. Therefore, NumberC will also have a type of double

auto GetDouble() {
    return 3.14;
}

auto Number { GetDouble() };

As of C++ 20, auto can also be used with function parameters:

auto Add(auto x, auto y) {
    return x + y;
}

auto Number { Add(1, 2) };

We're calling the add function with two integers, so x and y in that function will be integers.

Then, we are returning the result of summing two integers, therefore the return type of Add will be deduced to be an integer.

And finally, because Number is being initialised to that return value, its type will also be an integer.

Be Cautious with auto in Parameter Lists

There is some additional nuance when our functions have auto parameters.

Such a function isn't actually a function at all - it is a function template.

This is a topic that's slightly out of scope for this course - so I'd recommend avoiding using auto in this way for now until we understand what is going on.

We cover function templates in detail in the next course

C++ auto vs Dynamic Typing

Some may have experience with other programming languages, where we store data inside variables, regardless of the type of those variables.

This is sometimes called "dynamic typing". It is important to note that auto is not an implementation of dynamic types.

For example, this code will not compile:

auto MyVariable { 10 };
MyVariable = "Hello World"

The type of MyVariable is inferred to be an int as soon as line 1 is executed. It cannot later change to a string. Even though it's type is auto, that type is determined as soon as our variable is initialised.

For that reason, we also can't do this:

auto MyVariable;

If we try to use auto without initialising the variable, the compiler has no way to tell what to set its type to.

C++ is a strongly typed language. The type of variables needs to be known. auto doesn't change that, it just asks the compiler to figure that out for us.

Qualifiers: auto Pointers, auto References

As with any type, we can add "qualifiers" to our auto declarations. For example, we can create references and pointers to automatically deduced types:

int x { 5 };

// Reference to an automatically deduced type
auto& ReferenceToX { x };

// Pointer to an automatically deduced type
auto* PointerToX { &x };

Should We Use auto?

Generally auto should not be used if our only reason is to avoid specifying our data types.

Whilst it can save a few keystrokes, using auto can make our code more difficult to read, especially as it gets more complicated.

To figure out what type an auto variable is, we have to hover over it, rather than simply being able to read the type directly. Moreover, we don't always read code in an editor.

In larger projects, code is often read in tools that won't provide visibility on what "auto" is being interpreted as.

Secondly, even if our editors can show us what auto is being interpreted as, that capability often requires our code being in a compilable state.

If we introduce an issue that prevents code from being compilable, we often lose visibility on the types we were relying on being deduced by the compiler.

This means it can be very difficult to work with complex code that makes excessive use of auto.

However, there are some scenarios where using auto makes sense. Most notably, it could be used where it is extremely obvious what our type is, even without tool assistance.

For example, a past lesson had this code:

enum class Faction { Human, Elf, Undead };
Faction Faction { Faction::Undead };

Faction being repeated 3 times on this short line of code is quite messy. It can also be confusing for beginners, as both the name and type of the variable are the same.

The Faction:: syntax makes it clear this variable is going to be initialised from the Faction enum, so its type is obvious. Therefore, we might consider using auto in this scenario:

auto Faction { Faction::Human };

For similar reasons, another scenario where auto might be preferred is when we are storing the value returned from a named cast:

Monster* Monster { dynamic_cast<Monster*>(Target) };

Given we're already specifying the type we're trying to dynamic_cast to, it is reasonable to replace the duplicated type with auto:

auto Monster { dynamic_cast<Monster*>(Target) };

In the remainder of this course, and the more advanced courses, we adopt a fairly conservative use of auto. We avoid it by default, but will use it if we think it makes the code clearer.

This generally falls in line with Google's style guide:

Use type deduction only to make the code clearer or safer, and do not use it merely to avoid the inconvenience of writing an explicit type.

When judging whether the code is clearer, keep in mind that your readers are not necessarily on your team, or familiar with your project, so types that you experience as unnecessary clutter will very often provide useful information to others

However, many others recommend avoiding auto as much as possible:

Always be explicit about the type you're initializing. This means that the type must be plainly visible to the reader.

...

It's very important that types are clearly visible to someone who is reading the code. Even though some IDEs are able to infer the type, doing so relies on the code being in a compilable state. It also won't assist users of merge/diff tools, or when viewing individual source files in isolation, such as on GitHub.

Up next, we'll see a scenario where we need to use auto. We'll learn how we can use structured bindings to quickly create a large amount of variables from one of our objects.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Namespaces, Enums and Structs
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ Structured Binding

Learn some syntax added to C++ recently, which makes dealing with our structs a little cleaner
3D art showing a fantasy pirate character
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved