auto
What problem does this section solve?
In C++, many type names are long, difficult to type, and easy to mistype, for example:
std::map<std::string, std::vector<int>>::const_iterator it = m.begin();
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();
Allow the compiler to automatically deduce types for us, reducing verbose type declarations to make code cleaner and easier to maintain.
What is this feature?
The auto keyword tells the compiler: "Please help me automatically deduce the type of this variable based on the initialized value." It doesn't weaken type safety — the deduced type is determined at compile time, so it remains strongly typed.
C++ standard version
C++11 (basic usage), C++14 added auto for function return type deduction, and C++17 introduced auto for use in scenarios like structured bindings.
Required header files
No additional header files are required. auto is a language keyword.
Basic Syntax
auto 变量名 = 表达式; // 编译器根据表达式推导类型
const auto 变量名 = 表达式; // 推导后加 const
auto& 变量名 = 表达式; // 推导为引用类型
const auto& 变量名 = 表达式; // 推导为只读引用类型
Common Usage
|Usage|Explanation|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.
Example|
|:---|:---|:---|
|auto|Automatic value type deduction (loses references and const)|auto x = 42; → int|
|const auto|Derived as a read-only type.|const auto s = "hello";|
|auto&|Derived as reference (modifiable original value)|auto& x = vec[0];|
|const auto&|Derive as read-only reference (no copy, no modification)|const auto& x = get_value();|
|auto*|derived as a pointer|auto* p = &x;|
|decltype(auto)|Perfect forwarding with reference and cv-qualifier preservation (C++14)|decltype(auto) x = get_ref();|
When to use which type
auto The most commonly misunderstood aspect is: it doesn't "automatically become the type you want", but rather deduces according to fixed rules. Especially when encountering references, const, or function return values, you must clearly write out your intent.
| Scene | Recommended Writing Style | Reason |
|---|---|---|
| The local variable types are very obvious. | auto x = expr; | Avoid redundant types. |
| Traverse large objects in read-only mode. | const auto& x | Avoid copying, and do not allow modification |
| Modify the original object | auto& x | Get the citation. |
| nullable pointer | auto* p = get(); | Let the reader see at a glance that this is a pointer. |
| Functions return references and must retain references | decltype(auto) | auto will lose its reference. |
| The type itself carries business meaning. | Explicit Type | For example Meter distance = ... is clearer than auto distance |
Example code
Example 1: Basic Type Inference
#include <iostream>
#include <string>
int main()
{
auto n = 42; // int
auto d = 3.14; // double
auto c = 'A'; // char
auto s = "hello"; // const char*
auto str = std::string("world"); // std::string
std::cout << "n = " << n << "\n";
std::cout << "d = " << d << "\n";
std::cout << "c = " << c << "\n";
std::cout << "str = " << str << "\n";
return 0;
}
Results:
n = 42
d = 3.14
c = A
str = world
Example 2: Building on Example 1, the difference between auto& and const auto&.
#include <iostream>
int main()
{
int x = 10;
auto a = x; // a 是 int,拷贝了 x
auto& b = x; // b 是 int&,是 x 的引用
const auto& c = x; // c 是 const int&,只读引用
a = 100;
std::cout << "after a=100, x = " << x << "\n"; // x 不变
b = 200;
std::cout << "after b=200, x = " << x << "\n"; // x 变了
// c = 300; // ❌ 编译错误!c 是只读引用
return 0;
}
Results:
after a=100, x = 10
after b=200, x = 200
Example 3: Building on Example 2, use auto to simplify iterators and complex types
Using auto in C++ allows the compiler to deduce types automatically, which significantly reduces verbosity when working with complex types like iterators or deeply nested container types. Here’s how it simplifies the code from Example 2:
Example 2 (without auto):
std::map<std::string, std::vector<int>>::iterator it = myMap.begin();
while (it != myMap.end()) {
std::vector<int> vec = it->second;
for (std::vector<int>::iterator vecIt = vec.begin(); vecIt != vec.end(); ++vecIt) {
std::cout << *vecIt << std::endl;
}
++it;
}
Example 3 (with auto):
auto it = myMap.begin();
while (it != myMap.end()) {
auto vec = it->second;
for (auto vecIt = vec.begin(); vecIt != vec.end(); ++vecIt) {
std::cout << *vecIt << std::endl;
}
++it;
}
Key improvements:
- Iterator declarations become
auto it = container.begin()instead of spelling out the full type. - Complex nested types (like
std::vector<int>::iterator) are inferred automatically. - Code becomes more readable and less error-prone, especially when types are long or change.
- The underlying logic remains identical—
autoonly simplifies the syntax, not the behavior.
This is especially useful when working with templated or generic code where exact types might be cumbersome to write out explicitly.
#include <iostream>
#include <vector>
#include <map>
#include <memory>
#include <string>
int main()
{
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// 不用 auto 的写法(类型名很长)
for (std::vector<std::string>::const_iterator it = names.begin();
it != names.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << "\n";
// 用 auto 简化(同样清晰)
for (auto it = names.begin(); it != names.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << "\n";
// 智能指针用 auto 简化
auto ptr = std::make_shared<int>(42);
std::cout << "*ptr = " << *ptr << "\n";
return 0;
}
Results:
Alice Bob Charlie
Alice Bob Charlie
*ptr = 42
Example 4: Note copying vs referencing when using auto to iterate over containers
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// ❌ 错误用法:auto 会拷贝每个元素(低效)
std::cout << "auto (copies): ";
for (auto s : names)
{
s = "X"; // 修改的是拷贝,不影响原容器
}
for (auto s : names)
{
std::cout << s << " ";
}
std::cout << "(unchanged)\n";
// ✅ 正确用法:auto& 修改原容器中的元素
for (auto& s : names)
{
s = "X";
}
std::cout << "auto& (references): ";
for (auto s : names)
{
std::cout << s << " ";
}
std::cout << "(modified)\n";
return 0;
}
Results:
auto (copies): Alice Bob Charlie (unchanged)
auto& (references): X X X (modified)
示例 5:在示例 4 基础上,auto 返回值和 decltype(auto) 的区别(指针)
#include <iostream>
int global_score = 80;
int* score_ptr()
{
return &global_score;
}
// auto 推导指针返回类型时,推导结果就是 int*(指针本身被拷贝,但指向不变)
auto read_ptr()
{
return score_ptr();
}
// decltype(auto) 对非引用表达式,效果等同于 auto,结果同样是 int*
decltype(auto) borrow_ptr()
{
return score_ptr();
}
int main()
{
auto p1 = score_ptr(); // p1 是 int*,拷贝了"指针",但地址值相同
*p1 = 90;
std::cout << "after auto p1, *p1 = 90, global_score = " << global_score << "\n";
int*& p_ref = score_ptr(); // 这里会报错!score_ptr() 是右值,不能绑定到 int*& 上
// (此行仅作对比说明用,实际编译会失败,下方运行结果不包含它)
auto p2 = read_ptr(); // p2 也是 int*,read_ptr 本身也按值返回指针
*p2 = 100;
std::cout << "after auto p2, *p2 = 100, global_score = " << global_score << "\n";
decltype(auto) p3 = borrow_ptr(); // p3 仍是 int*,跟 auto 推导结果一样
*p3 = 110;
std::cout << "after decltype(auto) p3, *p3 = 110, global_score = " << global_score << "\n";
return 0;
}
Results:
after auto p1, *p1 = 90, global_score = 90
after auto p2, *p2 = 100, global_score = 100
after decltype(auto) p3, *p3 = 110, global_score = 110
示例 6:在示例 5 基础上,auto 返回值和 decltype(auto) 的区别(引用)
#include <iostream>
int global_score = 80;
int& score_ref()
{
return global_score;
}
// auto 作为函数返回类型时,会按"值"返回,引用会被丢掉
auto read_score()
{
return score_ref();
}
// decltype(auto) 会保留 score_ref() 的 int& 返回类型
decltype(auto) borrow_score()
{
return score_ref();
}
int main()
{
auto a = score_ref(); // a 是 int,拷贝
a = 90;
std::cout << "after auto a = 90, global_score = " << global_score << "\n";
auto& b = score_ref(); // b 是 int&,引用
b = 90;
std::cout << "after auto& b = 90, global_score = " << global_score << "\n";
auto c = read_score(); // c 是 int,read_score 本身也返回拷贝
c = 100;
std::cout << "after auto c = 100, global_score = " << global_score << "\n";
decltype(auto) d = borrow_score(); // d 是 int&,仍然引用 global_score
d = 100;
std::cout << "after decltype(auto) d = 100, global_score = " << global_score << "\n";
score_ref() = 110; // 直接对函数调用结果赋值,score_ref() 本身是左值
std::cout << "after score_ref() = 110, global_score = " << global_score << "\n";
// read_score() = 120; // 编译错误:read_score() 返回 int(按值),是右值,不能直接赋值
return 0;
}
Results:
after auto a = 90, global_score = 80
after auto& b = 90, global_score = 90
after auto c = 100, global_score = 90
after decltype(auto) d = 100, global_score = 100
after score_ref() = 110, global_score = 110
|问题|示例 5(引用)|示例 6(指针)|
|---|---|---|
|score_ref() / score_ptr() 返回什么|int&(绑定到 global_score 本身)|int*(一个保存 global_score 地址的值)|
|auto a = score_ref(); 拷贝的是什么|int 的值 → 和 global_score 脱钩|——|
|auto p1 = score_ptr(); 拷贝的是什么|——|"指针变量"这个值(地址)→ 但地址指向的还是 global_score|
|修改 a / *p1 能否影响 global_score|a = 90 不能|*p1 = 90 能|
|auto 经过函数包一层(read_score/read_ptr)后还能改到原变量吗|不能(引用性已丢失)|能(指针的指向关系从一开始就不依赖"引用性")|
|decltype(auto) 相比 auto 的意义|关键:保住了 int&,否则会退化成 int 拷贝|没有额外意义:函数返回的本来就是 int*(一个普通值),decltype(auto) 和 auto 推导结果完全一致|
auto 真正"丢掉"的东西只有一种:表达式的引用性(即"是否和某个已有对象绑定"这件事)。
- 对
score_ref()(返回int&)而言,引用性就是它的全部价值所在——一旦丢了,a就成了和原变量毫无关系的副本。decltype(auto)的作用就是把这层引用性保住。
- 对
score_ptr()(返回int*)而言,它本身就不是引用,是一个普通的值(哪怕这个值碰巧是个地址)。auto推导普通值类型时本来就不会发生"丢引用"的事,所以推导出来天然就是int*,和decltype(auto)推导结果没有任何区别。
真正决定"能不能改到原变量"的,从来不是 auto 还是 decltype(auto),而是你拷贝的这个值里,是否仍然携带着指向原对象的"路径":
- 拷贝
int值 —— 路径断了,改不到。
- 拷贝
int*(地址值) —— 路径还在,解引用之后照样能改到。
runtime results
See the "running results" for each example above.
Key syntax explanation in the example
|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.
| Example | Discusses what | Newly emerged syntax | Why write it this way | Precautions |
|---|---|---|---|---|
| Example 1 | Basic Type Deduction | auto x = 表达式 | In the simplest case, the type is determined by the initialization value. | String literal is inferred as const char* |
| Example 2 | auto vs auto& vs const auto& | auto&、const auto& | auto will lose references and top-level const; you must use auto& to reference the original variable. | Be careful when iterating through container elements. |
| Example 3 | Simplify complex types | Use auto with iterators and smart pointers | 当类型名过长时,可使用 auto 关键字保持代码的可读性。例如: |
// 不推荐:冗长的类型声明
std::vector<std::pair<int, std::string>>::iterator it = myVector.begin();
// 推荐:使用 auto
auto it = myVector.begin();
这样不仅使代码更简洁,还能在类型变更时减少维护成本。|auto doesn't weaken type safety; it just helps the compiler write the code for you.|
|Example 4|The copy trap of auto during iteration|Range-based for loops combined with auto or auto& in C++|Traverse and copy elements, auto& reference elements|To modify the original container, you must use auto&.|
|Example 5|auto returns value vs decltype(auto)|auto function return, decltype(auto)|When a function returns a reference, a normal auto will degrade to a value return.|decltype(auto) is quite powerful, but also make sure it won't return a dangling reference.|
Common Errors
Error 1: Thought that auto could automatically infer as a reference.
int x = 10;
auto y = x;
y = 20; // x 不会变!y 是拷贝不是引用
Correct approach: The original variable needs to be modified using auto&.
Error 2: Using auto to declare function parameters
void func(auto x) { ... } // C++20 之前是错误!(除非是泛型 lambda)
Correct approach: ordinary function parameters cannot use auto (C++20 allows it, but that's template syntax).
Error 3: auto cannot be used in a multi-variable declaration
auto x = 1, y = 3.14; // ❌ 编译错误!推导类型不一致
The correct approach: each auto variable is declared separately.
Error 4: Overusing 'auto' makes the business meaning disappear
auto timeout = 3000; // 3000 是毫秒?秒?次数?
The correct approach: Types or names should convey meaning. For example, std::chrono::milliseconds timeout{3000};, or at least auto timeout_ms = 3000;.
使用建议
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。
如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。
- Use auto wherever possible: reduce repetitive type declarations to make the code more concise.
- Iterate over containers using
const auto&(without modification) orauto&** (needs modification), to avoid accidental copying. - Places that clearly need references must include
auto&:autowill not automatically infer references. autocannot fully replace explicit types: When types are not obvious (such as inferring from a function's return value), writing types explicitly might be clearer.decltype(auto)should only be used when the reference truly needs to be preserved: it is more precise and makes it easier to expose dangling references.
Summary
autoallows compilers to automatically infer variable types, reducing the need for verbose type declarations.autoloses references and top-level const; to traverse a container, useconst auto&orauto&.- This is particularly suited for simplifying complex types such as iterators and smart pointers.
autofunctions return by value by default; in order to preserve reference semantics, usedecltype(auto).autois still strongly typed, and its type is determined at compile time.