Consider the following statement:
1 |
double d{ 5.0 }; |
If C++ already knows 5.0
is a double
literal, why do we have to explicitly specify that d
is actually a double
? Wouldn’t it be nice if we could tell a variable to just assume the proper type based on the value we’re initializing it with?
Type inference for initialized variables
When initializing a variable, the auto
keyword can be used in place of the type
to tell the compiler to infer the variable’s type from the initializer’s type. This is called type inference (also sometimes called type deduction).
For example:
1 2 |
auto d{ 5.0 }; // 5.0 is a double literal, so d will be type double auto i{ 1 + 2 }; // 1 + 2 evaluates to an int, so i will be type int |
This also works with the return values from functions:
1 2 3 4 5 6 7 8 9 10 |
int add(int x, int y) { return x + y; } int main() { auto sum { add(5, 6) }; // add() returns an int, so sum's type will be deduced to int return 0; } |
While using auto
in place of fundamental data types only saves a few (if any) keystrokes, in future lessons we will see examples where the types get complex and lengthy. In those cases, using auto
can save a lot of typing.
Type inference for functions
In C++14, the auto
keyword was extended to be able to deduce a function’s return type from return statements in the function body. Consider the following program:
1 2 3 4 |
auto add(int x, int y) { return x + y; } |
Since x + y
evaluates to an int
, the compiler will deduce this function should have a return type of int
. When using an auto
return type, all return statements must return the same type, otherwise an error will result.
While this may seem neat, we recommend that this syntax be avoided for normal functions. The return type of a function is of great use in helping to document for the caller what a function is expected to return. When a specific type isn’t specified, the caller may misinterpret what type the function will return, which can lead to inadvertent errors.
Best practice
Avoid using type inference for function return types.
Interested readers may wonder why using auto
when initializing variables is okay, but not recommended for function return types. A good rule of thumb is that auto
is okay to use when defining a variable, because the object the variable is inferring a type from is visible on the right side of the statement. However, with functions, that is not the case -- there’s no context to help indicate what type the function returns. A user would actually have to dig into the function body itself to determine what type the function returned. It’s much less intuitive, and therefore more error prone.
Trailing return type syntax
The auto
keyword can also be used to declare functions using a trailing return syntax, where the return type is specified after the rest of the function prototype.
Consider the following function:
1 2 3 4 |
int add(int x, int y) { return (x + y); } |
Using auto
, this could be equivalently written as:
1 2 3 4 |
auto add(int x, int y) -> int { return (x + y); } |
In this case, auto
does not perform type inference -- it is just part of the syntax to use a trailing return type.
Why would you want to use this?
One nice thing is that it makes all of your function names line up:
1 2 3 4 |
auto add(int x, int y) -> int; auto divide(double x, double y) -> double; auto printSomething() -> void; auto generateSubstring(const std::string &s, int start, int len) -> std::string; |
For now, we recommend the continued use of the traditional function return syntax. But we’ll see this trailing return type syntax crop up again in lesson 10.15 -- Introduction to lambdas (anonymous functions).
Type inference for function parameter types
Many new programmers try something like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> void addAndPrint(auto x, auto y) // only valid starting in C++20 { std::cout << x + y; } int main() { addAndPrint(2, 3); // int addAndPrint(4.5, 6.7); // double } |
Prior to C++20, this won’t work, because the compiler can’t infer types for function parameters x and y at compile time. Pre-C++20, if you’re looking to create generic functions that work with a variety of different types, you should be using function templates
(covered in a later chapter), not type inference
.
Starting in C++20, the auto
keyword can be used as a shorthand way to create function templates
, so the above code will compile and run.
For advanced readers
Lambda expressions
have supported auto
parameters since C++14. We’ll cover lambda expressions in a future lesson.
![]() |
![]() |
![]() |
Will auto ever make a variable an std::string or std::vector, etc.?
Sure. For example, if you initialize an auto variable with the results of a function returning one of those types...
I have a question. In python, there is a function called "type()" which resolves the type of an input. For example:
Output:
<class 'str'>
Means "hello" is an object of the string class. Is there any similar method in c++?
The return value of `name()` is implementation-defined. You can't use it for anything other than debugging.
Hello, I just wanted to know if we should be using function templets or the auto type deduction to make functions more flexible. Which one is preferred more starting C++20, also would using type deduction create portability issues? since not every one will be using a compiler that implements C++20 as its still fairly new.
Thanks.
i cant remember where to change or update my current C++ version to C++20
For this line of code in your last example -
You guys said "this use of auto does not perform type inference", but it looks like it does. Is there something I'm not getting?
I'm not sure what I was thinking when I wrote that. I've removed it for the time being.
The way C++ handles type deduction for auto variables is substantially similar to how it deduces types for templates.\
Thanks for the feedback.