1. 程式人生 > 其它 >auto與迭代器的用法_C++ auto型別推導完全攻略

auto與迭代器的用法_C++ auto型別推導完全攻略

技術標籤:auto與迭代器的用法

在C++11 之前的版本(C++98 和 C++ 03)中,定義變數或者宣告變數之前都必須指明它的型別,比如 int、char 等;但是在一些比較靈活的語言中,比如C#、JavaScript、PHP、Python等,程式設計師在定義變數時可以不指明具體的型別,而是讓編譯器(或者直譯器)自己去推導,這就讓程式碼的編寫更加方便。C++11 為了順應這種趨勢也開始支援自動型別推導了!C++11 使用auto關鍵字來支援自動型別推導。

auto 型別推導的語法和規則

在之前的 C++ 版本中,auto 關鍵字用來指明變數的儲存型別,它和 static 關鍵字是相對的。auto 表示變數是自動儲存的,這也是編譯器的預設規則,所以寫不寫都一樣,一般我們也不寫,這使得 auto 關鍵字的存在變得非常雞肋。C++11 賦予 auto 關鍵字新的含義,使用它來做自動型別推導。也就是說,使用了 auto 關鍵字以後,編譯器會在編譯期間自動推匯出變數的型別,這樣我們就不用手動指明變數的資料型別了。auto 關鍵字基本的使用語法如下:

auto name = value;

name 是變數的名字,value 是變數的初始值。注意:auto 僅僅是一個佔位符,在編譯器期間它會被真正的型別所替代。或者說,C++ 中的變數必須是有明確型別的,只是這個型別是由編譯器自己推匯出來的。auto 型別推導的簡單例子:

auto n = 10;
auto f = 12.8;
auto p = &n;
auto url = "http://c.biancheng.net/cplus/";

下面我們來解釋一下:

  • 第 1 行中,10 是一個整數,預設是 int 型別,所以推匯出變數 n 的型別是 int。

  • 第 2 行中,12.8 是一個小數,預設是 double 型別,所以推匯出變數 f 的型別是 double。

  • 第 3 行中,&n 的結果是一個 int* 型別的指標,所以推匯出變數 f 的型別是 int*。

  • 第 4 行中,由雙引號""包圍起來的字串是 const char* 型別,所以推匯出變數 url 的型別是 const char*,也即一個常量指標。

我們也可以連續定義多個變數:

int n = 20;
auto *p = &n, m = 99;

先看前面的第一個子表示式,&n 的型別是 int*,編譯器會根據 auto *p 推匯出 auto 為 int。後面的 m 變數自然也為 int 型別,所以把 99 賦值給它也是正確的。這裡我們要注意,推導的時候不能有二義性。在本例中,編譯器根據第一個子表示式已經推匯出 auto 為 int 型別,那麼後面的 m 也只能是 int 型別,如果寫作m=12.5

就是錯誤的,因為 12.5 是double 型別,這和 int 是衝突的。還有一個值得注意的地方是:使用 auto 型別推導的變數必須馬上初始化,這個很容易理解,因為 auto 在 C++11 中只是“佔位符”,並非如 int 一樣的真正的型別宣告。

auto 的高階用法

auto 除了可以獨立使用,還可以和某些具體型別混合使用,這樣 auto 表示的就是“半個”型別,而不是完整的型別。請看下面的程式碼:

int  x = 0;auto *p1 = &x;   //p1 為 int *,auto 推導為 intauto  p2 = &x;   //p2 為 int*,auto 推導為 int*auto &r1  = x;   //r1 為 int&,auto 推導為 intauto r2 = r1;    //r2 為  int,auto 推導為 int

下面我們來解釋一下:

  • 第 2 行程式碼中,p1 為 int* 型別,也即 auto * 為 int *,所以 auto 被推導成了 int 型別。

  • 第 3 行程式碼中,auto 被推導為 int* 型別,前邊的例子也已經演示過了。

  • 第 4 行程式碼中,r1 為 int & 型別,auto 被推導為 int 型別。

  • 第 5 行程式碼是需要重點說明的,r1 本來是 int& 型別,但是 auto 卻被推導為 int 型別,這表明當=右邊的表示式是一個引用型別時,auto 會把引用拋棄,直接推匯出它的原始型別。

接下來,我們再來看一下 auto 和 const 的結合:

int  x = 0;const  auto n = x;  //n 為 const int ,auto 被推導為 intauto f = n;      //f 為 const int,auto 被推導為 int(const 屬性被拋棄)const auto &r1 = x;  //r1 為 const int& 型別,auto 被推導為 intauto &r2 = r1;  //r1 為 const int& 型別,auto 被推導為 const int 型別

下面我們來解釋一下:

  • 第 2 行程式碼中,n 為 const int,auto 被推導為 int。

  • 第 3 行程式碼中,n 為 const int 型別,但是 auto 卻被推導為 int 型別,這說明當=右邊的表示式帶有 const 屬性時, auto 不會使用 const 屬性,而是直接推匯出 non-const 型別。

  • 第 4 行程式碼中,auto 被推導為 int 型別,這個很容易理解,不再贅述。

  • 第 5 行程式碼中,r1 是 const int & 型別,auto 也被推導為 const int 型別,這說明當 const 和引用結合時,auto 的推導將保留表示式的 const 型別。

最後我們來簡單總結一下 auto 與 const 結合的用法:

  • 當型別不為引用時,auto 的推導結果將不保留表示式的 const 屬性;

  • 當型別為引用時,auto 的推導結果將保留表示式的 const 屬性。

auto 的限制

前面介紹推導規則的時候我們說過,使用 auto 的時候必須對變數進行初始化,這是 auto 的限制之一。那麼,除此以外,auto 還有哪些其它的限制呢?1) auto 不能在函式的引數中使用。這個應該很容易理解,我們在定義函式的時候只是對引數進行了宣告,指明瞭引數的型別,但並沒有給它賦值,只有在實際呼叫函式的時候才會給引數賦值;而 auto 要求必須對變數進行初始化,所以這是矛盾的。2) auto 不能作用於類的非靜態成員變數(也就是沒有 static 關鍵字修飾的成員變數)中。3) auto 關鍵字不能定義陣列,比如下面的例子就是錯誤的:

char url[] = "http://c.biancheng.net/";
auto str[] = url; //arr 為陣列,所以不能使用 auto

4) auto 不能作用於模板引數,請看下面的例子:

template <typename T>class A{    //TODO:};int  main(){    A<int> C1;    A<auto> C2 = C1;  //錯誤    return 0;}

auto 的應用

說了那麼多 auto 的推導規則和一些注意事項,那麼 auto 在實際開發中到底有什麼應用呢?下面我們列舉兩個典型的應用場景。

使用 auto 定義迭代器

auto 的一個典型應用場景是用來定義 stl 的迭代器。我們在使用 stl 容器的時候,需要使用迭代器來遍歷容器裡面的元素;不同容器的迭代器有不同的型別,在定義迭代器時必須指明。而迭代器的型別有時候比較複雜,書寫起來很麻煩,請看下面的例子:

#include using namespace std;int main(){    vector< vector<int> > v;    vector< vector<int> >::iterator i = v.begin();    return 0;}

可以看出來,定義迭代器 i 的時候,型別書寫比較冗長,容易出錯。然而有了 auto 型別推導,我們大可不必這樣,只寫一個 auto 即可。

修改上面的程式碼,使之變得更加簡潔:

#include using namespace std;int main(){    vector< vector<int> > v;    auto i = v.begin();  //使用 auto 代替具體的型別    return 0;}

auto 可以根據表示式 v.begin() 的型別(begin() 函式的返回值型別)來推匯出變數 i 的型別。

auto 用於泛型程式設計

auto 的另一個應用就是當我們不知道變數是什麼型別,或者不希望指明具體型別的時候,比如泛型程式設計中。我們接著看例子:

#include using namespace std;class A{public:    static int get(void){        return 100;    }};class B{public:    static const char* get(void){        return "http://c.biancheng.net/cplus/";    }};template <typename T>void func(void){    auto val = T::get();    cout << val << endl;}int main(void){    func();    func();    return 0;}

執行結果:

100
http://c.biancheng.net/cplus/

本例中的模板函式 func() 會呼叫所有類的靜態函式 get(),並對它的返回值做統一處理,但是 get() 的返回值型別並不一樣,而且不能自動轉換。這種要求在以前的 C++ 版本中實現起來非常的麻煩,需要額外增加一個模板引數,並在呼叫時手動給該模板引數賦值,用以指明變數 val 的型別。但是有了 auto 型別自動推導,編譯器就根據 get() 的返回值自己推匯出 val 變數的型別,就不用再增加一個模板引數了。下面的程式碼演示了不使用 auto 的解決辦法:

#include using namespace std;class A{public:    static int get(void){        return 100;    }};class B{public:    static const char* get(void){        return "http://c.biancheng.net/cplus/";    }};template <typename T1, typename T2>  //額外增加一個模板引數 T2void func(void){    T2 val = T1::get();    cout << val << endl;}int main(void){    //呼叫時也要手動給模板引數賦值    funcint>();    funcconst     return 0;}

ccc0606f2df566a1467a4474e90e094d.png