C++11 被譽為「如同一門新語言」,解決了 C++98 眾多歷史遺留問題。雖早期編譯器支持不全,但提前掌握核心特性至關重要。本系列逐一講解,本文聚焦類型推導、遍歷簡化與空指針字面值。
C++98 auto 表示自動存儲期(默認行為),幾乎無人使用。C++11 重新定義:auto 讓編譯器從初始化表達式推導變量類型。
auto a = 1; // int
auto b = 1.5; // double
auto c = "text"; // const char*
auto d = vector{1,2,3}; // vector
auto it = vec.begin(); 替代 vector::iterator it = vec.begin(); double,用戶用 auto 自動匹配,不會誤寫成 float。auto 配合尾置返回類型實現完全泛化。| 規則 | 說明 | 示例 |
|---|---|---|
| 必須初始化 | 無初始化無法推導 | auto x; ❌ |
| 忽略頂層 const/volatile | const int ci=0; auto a=ci; → int | 保留底層 const:const int* p; auto q=p; → const int* |
| 引用需顯式 & | auto& r = x; 推導引用類型 | auto r = x; 總是值拷貝 |
| 同一聲明多變量類型必須一致 | 按首個變量推導類型 | auto x=1, y=2.0; ❌ |
| 不可用於函數參數 | 模板參數用 typename | void f(auto x); ❌ |
| 不可用於非靜態成員聲明 | 編譯期無法推導 | struct S { auto m=1; }; ❌ |
| 不可定義數組 | 用 vector 或 array | auto arr[3]={1,2,3}; ❌ |
| 不可作為模板實參 | vector ❌ |
int i=0;
decltype(i) a; // int (無需初始化)
decltype((i)) b = i; // int& (表達式是左值)
decltype(i++) c; // int (表達式是右值)
設表達式 e 類型為 T:
T(如 decltype(i)、decltype(s.member))T&&(如 decltype(std::move(x))、decltype(f()) 當 f 返回 T&&)T&(如 decltype((i))、decltype(*p)、decltype(a[0]))T(如 decltype(1)、decltype(f()) 返回非引用)i vs (i)int i=0;
decltype(i) a = i; // int
decltype((i)) b = i; // int& (左值表達式)
b = 10; // 修改 i
// decltype((i)) c = 1; // ❌ 不能將右值綁定到非 const 左值引用
auto 丟棄頂層 const/volatile,decltype 完整保留:
const int ci = 0;
auto a = ci; // int
decltype(ci) b = 0; // const int
struct S { int m; };
const S cs;
decltype(cs.m) d; // int (成員不繼承類的 const)
T& & → T&、T& && → T&、T&& & → T&、T&& && → T&&。這是 std::forward 完美轉發的類型系統基石。
// C++98 無法寫出
template
??? add(T1 a, T2 b) { return a+b; }
template
auto add(T1 a, T2 b) -> decltype(a+b) { return a+b; }
auto 佔位,參數已進作用域-> decltype(expr) 利用參數推導返回類型auto Class::func() -> ReturnTypeauto forward(Args&&... args) -> decltype(target(std::forward(args)...)) for (declaration : range) statement;
// range: 數組、有 begin/end 的類、初始化列表
int a[] = {1,2,3,4,5};
for (int& x : a) x *= 2; // 修改元素需引用
for (const int& x : a) cout << x; // 僅讀用 const 引用避免拷貝
for (auto x : a) cout << x; // 拷貝,適合小型類型
vector v = {1,2,3};
for (auto& x : v) x *= 2; // vector 同理
// auto 推導非引用,修改無效
begin()/end() 的類++、*、==/!=void f(int arr[]) { // 實為 int*
for (auto x : arr) {} // ❌ 編譯錯誤:無法確定範圍
}
解決:傳 std::array、std::vector 或 span (C++20)。
#define NULL 0 或 0L,本質是整數,導致重載決議混淆:
void f(int); void f(char*);
f(NULL); // 調用 f(int) 而非 f(char*)!
std::nullptr_t,隱式轉換為任意指針/指針成員類型f(nullptr) 正確調用 f(char*)int (*(*pf())())(); // 返回「指向「返回「指向 int 的函數」的指針」的函數」的函數
pf 是標識符() → pf 是函數,無參數* → 返回指針() → 指向函數,無參數* → 返回指針() → 指向函數,無參數int → 最終返回 int即:pf 是函數,返回指向「函數(無參,返回指向「函數(無參,返回 int)的指針」)」的指針。
auto pf() -> int (*(*)())(); // 可讀性大幅提升
// 或分步別名
using FP = int(*)();
using FFP = FP(*)();
auto pf() -> FFP;
掌握 auto/decltype 推導規則、尾置返回類型、範圍 for 與 nullptr,是寫出現代、安全、可維護 C++ 代碼的入場券。下一篇將講解 override/final、強類型枚舉、統一初始化列表等特性。