Wiki
auto
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
1.本節解決什麼問題
在 C++ 中,很多型別的名字很長、很難寫,也容易寫錯,比如:
std::map<std::string, std::vector<int>>::const_iterator it = m.begin();
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();
auto 讓編譯器自動幫我們推導型別,減少冗長的型別宣告,讓程式碼更簡潔、更易維護。
2.這個特性是什麼
auto 關鍵字告訴編譯器:"請幫我根據初始化的值,自動推匯出這個變數的型別"。它並不是弱化型別安全——推匯出來的型別在編譯時就已經確定,所以仍然是強型別的。
3.C++ 標準版本
C++11(基礎用法),C++14 增加了 auto 作為函式返回型別推導,C++17 增加了 auto 在結構化繫結等場景中的使用。
4.需要的標頭檔案
不需要額外標頭檔案。auto 是語言關鍵字。
5.基本語法
auto 变量名 = 表达式; // 编译器根据表达式推导类型
const auto 变量名 = 表达式; // 推导后加 const
auto& 变量名 = 表达式; // 推导为引用类型
const auto& 变量名 = 表达式; // 推导为只读引用类型
6.常用用法
| 用法 | 說明 | 示例 |
|---|---|---|
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(); |
7.什麼時候用哪一種
auto 最容易被誤解的地方是:它不是"自動變成你想要的型別",而是按固定規則推導。尤其遇到引用、const、函式返回值時,要主動寫清楚你的意圖。
| 場景 | 推薦寫法 | 原因 |
|---|---|---|
| 區域性變數型別很明顯 | auto x = expr; |
少寫重複型別 |
| 只讀遍歷大物件 | const auto& x |
避免複製,同時不允許修改 |
| 要修改原物件 | auto& x |
明確拿到引用 |
| 可能為空的指標 | auto* p = get(); |
讓讀者一眼看出這是指標 |
| 函式返回引用且必須保留引用 | decltype(auto) |
auto 返回值會丟失引用 |
| 型別本身是業務含義 | 顯式型別 | 例如 Meter distance = ... 比 auto distance 更清楚 |
8.示例程式碼
8.1.示例 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
8.2.示例 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
8.3.示例 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
8.4.示例 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)
8.5.示例 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;
}
執行結果:
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
8.6.示例 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;
}
執行結果:
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*(地址值) —— 路徑還在,解引用之後照樣能改到。
9.執行結果
見上方每個示例的"執行結果"。
10.示例中的關鍵語法解釋
| 示例 | 講了什麼 | 新出現的語法 | 為什麼這樣寫 | 注意事項 |
|---|---|---|---|---|
| 示例 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) 很強,但也要確認不會返回懸垂引用 |
11.常見錯誤
錯誤 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;。
12.使用建議
- 能用 auto 的地方儘量用:減少重複型別宣告,讓程式碼更簡潔。
- 遍歷容器要用
const auto&(不修改)或auto&(需要修改),避免意外複製。 - 明確需要引用的地方要寫
auto&:auto不會自動推匯出引用。 auto不能完全替代顯式型別:當型別不明顯時(如從函式返回值推導),顯式寫型別可能更清晰。decltype(auto)只在確實要保留引用時使用:它更精確,也更容易把懸垂引用暴露出來。
13.小結
auto讓編譯器自動推導變數型別,減少冗長型別宣告。auto會丟失引用和頂層 const,遍歷容器要用const auto&或auto&。- 特別適合簡化迭代器、智慧指標等複雜型別。
auto函式返回值預設按值返回;要保留引用語義時用decltype(auto)。auto仍然是強型別的,編譯期型別就確定了。