首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++11 新特性深度解析(一):auto、decltype、範圍 for 與 nullptr

     編輯:關於C++

前言

C++11 被譽為「如同一門新語言」,解決了 C++98 眾多歷史遺留問題。雖早期編譯器支持不全,但提前掌握核心特性至關重要。本系列逐一講解,本文聚焦類型推導、遍歷簡化與空指針字面值。

一、 auto:編譯期類型推導佔位符

1. 從「存儲期關鍵字」到「推導佔位符」

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

2. auto 的三大優勢

  1. 簡化複雜類型聲明:auto it = vec.begin(); 替代 vector::iterator it = vec.begin();
  2. 避免隱式轉換精度損失:庫作者返回 double,用戶用 auto 自動匹配,不會誤寫成 float。
  3. 支撐泛型編程:模板中返回類型依賴參數時,auto 配合尾置返回類型實現完全泛化。

3. 使用細則與限制

規則說明示例
必須初始化無初始化無法推導auto x; ❌
忽略頂層 const/volatileconst 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; ❌
不可用於函數參數模板參數用 typenamevoid f(auto x); ❌
不可用於非靜態成員聲明編譯期無法推導struct S { auto m=1; }; ❌
不可定義數組用 vector 或 arrayauto arr[3]={1,2,3}; ❌
不可作為模板實參vector ❌

二、 decltype:從表達式推導類型

1. 動機:auto 推導自初始化表達式,decltype 推導自任意表達式

int i=0;
decltype(i) a;        // int (無需初始化)
decltype((i)) b = i;  // int& (表達式是左值)
decltype(i++) c;      // int (表達式是右值)

2. 四大推導規則(核心)

設表達式 e 類型為 T:

  1. 無括號標識符/類成員訪問 → T(如 decltype(i)、decltype(s.member))
  2. 將亡值 → T&&(如 decltype(std::move(x))、decltype(f()) 當 f 返回 T&&)
  3. 左值 → T&(如 decltype((i))、decltype(*p)、decltype(a[0]))
  4. 其他(純右值) → T(如 decltype(1)、decltype(f()) 返回非引用)

3. 關鍵區別:i vs (i)

int i=0;
decltype(i)  a = i;  // int
decltype((i)) b = i; // int& (左值表達式)
b = 10; // 修改 i
// decltype((i)) c = 1; // ❌ 不能將右值綁定到非 const 左值引用

4. 保留 cv 資格符

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)

5. 折疊規則:引用合併

T& & → T&、T& && → T&、T&& & → T&、T&& && → T&&。這是 std::forward 完美轉發的類型系統基石。

三、 尾置返回類型:auto + decltype 實現完全泛化

痛點:模板返回類型依賴參數

// C++98 無法寫出
template
??? add(T1 a, T2 b) { return a+b; }

C++11 解法

template
auto add(T1 a, T2 b) -> decltype(a+b) { return a+b; }
  • auto 佔位,參數已進作用域
  • -> decltype(expr) 利用參數推導返回類型
  • 簡化作用域前綴:auto Class::func() -> ReturnType
  • 廣泛用於轉發函數:auto forward(Args&&... args) -> decltype(target(std::forward(args)...))

四、 範圍 for:告別邊界與迭代器細節

語法

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 推導非引用,修改無效

使用條件

  1. 範圍可確定:數組、標準容器、擁有 begin()/end() 的類
  2. 迭代器支持 ++、*、==/!=

陷阱:數組退化為指針

void f(int arr[]) { // 實為 int*
    for (auto x : arr) {} // ❌ 編譯錯誤:無法確定範圍
}

解決:傳 std::array、std::vector 或 span (C++20)。

五、 nullptr:類型安全的空指針字面值

NULL 的缺陷

#define NULL 0 或 0L,本質是整數,導致重載決議混淆:

void f(int); void f(char*);
f(NULL); // 調用 f(int) 而非 f(char*)!

nullptr 解決

  • 類型 std::nullptr_t,隱式轉換為任意指針/指針成員類型
  • 不 隱式轉換為整數
  • f(nullptr) 正確調用 f(char*)

六、 經典面試題:解讀複雜函數指針聲明

int (*(*pf())())(); // 返回「指向「返回「指向 int 的函數」的指針」的函數」的函數

逐層拆解(由內向外,右左原則)

  1. pf 是標識符
  2. () → pf 是函數,無參數
  3. * → 返回指針
  4. () → 指向函數,無參數
  5. * → 返回指針
  6. () → 指向函數,無參數
  7. int → 最終返回 int

即:pf 是函數,返回指向「函數(無參,返回指向「函數(無參,返回 int)的指針」)」的指針。

C++11 尾置返回類型重寫

auto pf() -> int (*(*)())(); // 可讀性大幅提升
// 或分步別名
using FP = int(*)();
using FFP = FP(*)();
auto pf() -> FFP;

總結

掌握 auto/decltype 推導規則、尾置返回類型、範圍 for 與 nullptr,是寫出現代、安全、可維護 C++ 代碼的入場券。下一篇將講解 override/final、強類型枚舉、統一初始化列表等特性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved