第 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*
示例 2auto vs auto& vs const auto&auto&const auto&auto 會丟失引用和頂層 const,要用 auto& 才能引用原變量遍歷容器元素時要注意
示例 3簡化複雜類型迭代器、智能指針用 auto類型名太長時用 auto 保持可讀性auto 不弱化類型安全,只是讓編譯器幫你寫
示例 4auto 遍歷時的拷貝陷阱範圍 for + auto/auto&auto 遍歷拷貝元素,auto& 引用元素想修改原容器必須用 auto&
示例 5auto 返回值 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;

使用建議

  1. 能用 auto 的地方儘量用:減少重複類型聲明,讓代碼更簡潔。
  2. 遍歷容器要用 const auto&(不修改)或 auto&(需要修改),避免意外拷貝。
  3. 明確需要引用的地方要寫 auto&auto 不會自動推導出引用。
  4. auto 不能完全替代顯式類型:當類型不明顯時(如從函數返回值推導),顯式寫類型可能更清晰。
  5. decltype(auto) 只在確實要保留引用時使用:它更精確,也更容易把懸垂引用暴露出來。

小結

  • auto 讓編譯器自動推導變量類型,減少冗長類型聲明。
  • auto 會丟失引用和頂層 const,遍歷容器要用 const auto&auto&
  • 特別適合簡化迭代器、智能指針等複雜類型。
  • auto 函數返回值默認按值返回;要保留引用語義時用 decltype(auto)
  • auto 仍然是強類型的,編譯期類型就確定了。
音乐页