第 18.1 節
auto
0瀏覽次數0訪問次數--跳出率--平均停留
本節解決什麼問題
在 C++ 中,很多類型的名字很長、很難寫,也容易寫錯,比如:
std::map<std::string, std::vector<int>>::const_iterator it = m.begin();
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();
auto 讓編譯器自動幫我們推導類型,減少冗長的類型聲明,讓代碼更簡潔、更易維護。
這個特性是什麼
auto 關鍵字告訴編譯器:"請幫我根據初始化的值,自動推導出這個變量的類型"。它並不是弱化類型安全——推導出來的類型在編譯時就已經確定,所以仍然是強類型的。
C++ 標準版本
C++11(基礎用法),C++14 增加了 auto 作為函數返回類型推導,C++17 增加了 auto 在結構化綁定等場景中的使用。
需要的頭文件
不需要額外頭文件。auto 是語言關鍵字。
基本語法
auto 变量名 = 表达式; // 编译器根据表达式推导类型
const auto 变量名 = 表达式; // 推导后加 const
auto& 变量名 = 表达式; // 推导为引用类型
const auto& 变量名 = 表达式; // 推导为只读引用类型
常用用法
| 用法 | 說明 | 示例 |
|---|---|---|
auto | 自動推導值類型(會丟失引用和 const) | auto x = 42; → int |
const auto | 推導為只讀類型 | const auto s = "hello"; |
auto& | 推導為引用(可修改原值) | auto& x = vec[0]; |
const auto& | 推導為只讀引用(不拷貝、不可修改) | const auto& x = get_value(); |
auto* | 推導為指針 | auto* p = &x; |
decltype(auto) | 保留引用和 cv 限定的完美轉發(C++14) | decltype(auto) x = get_ref(); |
什麼時候用哪一種
auto 最容易被誤解的地方是:它不是"自動變成你想要的類型",而是按固定規則推導。尤其遇到引用、const、函數返回值時,要主動寫清楚你的意圖。
| 場景 | 推薦寫法 | 原因 |
|---|---|---|
| 局部變量類型很明顯 | auto x = expr; | 少寫重複類型 |
| 只讀遍歷大對象 | const auto& x | 避免拷貝,同時不允許修改 |
| 要修改原對象 | auto& x | 明確拿到引用 |
| 可能為空的指針 | auto* p = get(); | 讓讀者一眼看出這是指針 |
| 函數返回引用且必須保留引用 | decltype(auto) | auto 返回值會丟失引用 |
| 類型本身是業務含義 | 顯式類型 | 例如 Meter distance = ... 比 auto distance 更清楚 |
示例代碼
示例 1:基本類型推導
#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;
}
運行結果:
n = 42
d = 3.14
c = A
str = world
示例 2:在示例 1 基礎上,auto& 和 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;
}
運行結果:
after a=100, x = 10
after b=200, x = 200
示例 3:在示例 2 基礎上,用 auto 簡化迭代器和複雜類型
#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;
}
運行結果:
Alice Bob Charlie
Alice Bob Charlie
*ptr = 42
示例 4:用 auto 遍歷容器時注意拷貝 vs 引用
#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;
}
運行結果:
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_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";
return 0;
}
運行結果:
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
運行結果
見上方每個示例的"運行結果"。
示例中的關鍵語法解釋
| 示例 | 講了什麼 | 新出現的語法 | 為什麼這樣寫 | 注意事項 |
|---|---|---|---|---|
| 示例 1 | 基本類型推導 | auto x = 表达式 | 最簡單的情況,類型由初始化值決定 | 字符串字面量推導為 const char* |
| 示例 2 | auto vs auto& vs const auto& | auto&、const auto& | auto 會丟失引用和頂層 const,要用 auto& 才能引用原變量 | 遍歷容器元素時要注意 |
| 示例 3 | 簡化複雜類型 | 迭代器、智能指針用 auto | 類型名太長時用 auto 保持可讀性 | auto 不弱化類型安全,只是讓編譯器幫你寫 |
| 示例 4 | auto 遍歷時的拷貝陷阱 | 範圍 for + auto/auto& | auto 遍歷拷貝元素,auto& 引用元素 | 想修改原容器必須用 auto& |
| 示例 5 | auto 返回值 vs decltype(auto) | auto 函數返回、decltype(auto) | 函數返回引用時,普通 auto 會退化成值返回 | decltype(auto) 很強,但也要確認不會返回懸垂引用 |
常見錯誤
錯誤 1:以為 auto 能自動推導為引用
int x = 10;
auto y = x;
y = 20; // x 不会变!y 是拷贝不是引用
正確做法:需要修改原變量用 auto&。
錯誤 2:用 auto 聲明函數參數
void func(auto x) { ... } // C++20 之前是错误!(除非是泛型 lambda)
正確做法:普通函數參數不能用 auto(C++20 允許,但那是模板語法)。
錯誤 3:auto 不能用於多變量聲明
auto x = 1, y = 3.14; // ❌ 编译错误!推导类型不一致
正確做法:每個 auto 變量各自聲明。
錯誤 4:濫用 auto 讓業務含義消失
auto timeout = 3000; // 3000 是毫秒?秒?次数?
正確做法:類型或命名要能表達含義。例如 std::chrono::milliseconds timeout{3000};,或者至少寫成 auto timeout_ms = 3000;。
使用建議
- 能用 auto 的地方儘量用:減少重複類型聲明,讓代碼更簡潔。
- 遍歷容器要用
const auto&(不修改)或auto&(需要修改),避免意外拷貝。 - 明確需要引用的地方要寫
auto&:auto不會自動推導出引用。 auto不能完全替代顯式類型:當類型不明顯時(如從函數返回值推導),顯式寫類型可能更清晰。decltype(auto)只在確實要保留引用時使用:它更精確,也更容易把懸垂引用暴露出來。
小結
auto讓編譯器自動推導變量類型,減少冗長類型聲明。auto會丟失引用和頂層 const,遍歷容器要用const auto&或auto&。- 特別適合簡化迭代器、智能指針等複雜類型。
auto函數返回值默認按值返回;要保留引用語義時用decltype(auto)。auto仍然是強類型的,編譯期類型就確定了。