C 新特性之自動型別推斷和初始化列表

2021-10-21 23:44:02 字數 4829 閱讀 2723

auto:

就是編譯器能夠根據表示式的型別,自動決定變數的型別(從 c++14 開始,還有函式的返回型別),不再需要程式設計師手工宣告([1])。但需要說明的是,auto 並沒有改變 c++ 是靜態型別語言這一事實——使用 auto 的變數(或函式返回值)的型別仍然是編譯時就確定了,只不過編譯器能自動幫你填充而已。

例1:容器迭代器型別:

// vectorv;

for(vector<

int>

::iterator

it = v.

begin()

, end = v.

end();

it != end;

++it)

for

(auto it = v.

begin()

, end = v.

end();

it != end;

++it)

例2:未知型別:

不使用自動型別推斷時,如果容器型別未知的話,我們還需要加上 typename(注意此處 const 引用還要求我們寫 const_iterator 作為迭代器的型別):

template

<

typename t>

void

foo(

const t& container)

如果 begin 返回的型別不是該型別的 const_iterator 巢狀型別的話,那實際上不用自動型別推斷就沒法表達了。這還真不是假設。比如,如果我們的遍歷函式要求支援 c 陣列的話,不用自動型別推斷的話,就只能使用兩個不同的過載:

template

<

typename t, std::size_t n>

void

foo(

const t (

&a)[n])}

template

<

typename t>

void

foo(

const t& c)

}

如果使用自動型別推斷的話,再加上 c++11 提供的全域性 begin 和 end 函式,上面的**可以統一成:

template

<

typename t>

void

foo(

const t& c)

}

自動型別推導的優點:

自動型別推斷不僅降低了**的囉嗦程度,也提高了**的抽象性,使我們可以用更少的**寫出通用的功能。

auto的實際使用規則類似於函式模板引數的推導規則:

1. auto a = expr; 意味著用 expr 去匹配乙個假想的 template f(t) 函式模板,結果為值型別。

2. const auto& a = expr; 意味著用 expr 去匹配乙個假想的 template f(const t&) 函式模板,結果為常左值引用型別。

3. auto&& a = expr; 意味著用 expr 去匹配乙個假想的 template f(t&&) 函式模板,結果是乙個跟 expr 值類別相同的引用型別。

decltype:

decltype 的用途是獲得乙個表示式的型別,結果可以跟型別一樣使用。它有兩個基本用法:

1. decltype(變數名) 可以獲得變數的精確型別。

2. decltype(表示式) (表示式不是變數名,但包括 decltype((變數名)) 的情況)可以獲得表示式的引用型別;除非表示式的結果是個純右值(prvalue),此時結果仍然是值型別。

如果我們有 int a;,那麼:

* decltype(a) 會獲得 int(因為 a 是 int)。

* decltype((a)) 會獲得 int&(因為 a 是 lvalue)。

* decltype(a + a) 會獲得 int(因為 a + a 是 prvalue)。

通常情況下,能寫 auto 來宣告變數肯定是件比較輕鬆的事。但這兒有個限制,你需要在寫下 auto 時就決定你寫下的是個引用型別還是值型別。根據型別推導規則,auto 是值型別,auto& 是左值引用型別,auto&& 是**引用(可以是左值引用,也可以是右值引用)。使用 auto 不能通用地根據表示式型別來決定返回值的型別。不過,decltype(expr) 既可以是值型別,也可以是引用型別。因此,我們可以這麼寫:

c++14 引入了 decltype(auto) 語法:

decltype

(auto

) a = expr;

這種**主要用在通用的**函式模板中:你可能根本不知道你呼叫的函式是不是會返回乙個引用。這時使用這種語法就會方便很多。

類模板的模板引數推導:

如果你用過 pair 的話,一般都不會使用下面這種形式:

pair<

int,

int> pr

;

auto pr =

make_pair(1

,42);

在進入了 c++17 的世界後,這類函式變得不必要了。現在我們可以直接寫:

pair pr

;

array自動型別推導:

int a1=

;array<

int,

3> a2

;// 囉嗦

// arraya3; 不行

c++17:

array a

;// 得到 array

結構化繫結:

c++17前:

multimapint>

::iterator lower, upper;

std::

tie(lower, upper)

= mmp.

equal_range

("four"

);

c++17後:

auto

[lower, upper]

= mmp.

equal_range

("four"

);

c98:

vector<

int> v;

v.push(1

);v.push(2

);v.push(3

);v.push(4

);v.push(5

);

c++11:

vector<

int> v

;

統一初始化:

在**裡使用了大括號 {} 來進行物件的初始化。這當然也是 c++11 引入的新語法,能夠代替很多小括號 () 在變數初始化時使用。這被稱為統一初始化(uniform initialization)。

如果乙個類既有使用初始化列表的建構函式,又有不使用初始化列表的建構函式,那編譯器會千方百計地試圖呼叫使用初始化列表的建構函式,導致各種意外。所以,如果給乙個推薦的話,那就是:

* 如果乙個類沒有使用初始化列表的建構函式時,初始化該類物件可全部使用統一初始化語法。

* 如果乙個類有使用初始化列表的建構函式時,則只應用在初始化列表構造的情況。

類資料成員的預設初始化

按照 c++98 的語法,資料成員可以在建構函式裡進行初始化。這本身不是問題,但實踐中,如果資料成員比較多、建構函式又有多個的話,逐個去初始化是個累贅,並且很容易在增加資料成員時漏掉在某個建構函式中進行初始化。為此,c++11 增加了乙個語法,允許在宣告資料成員時直接給予乙個初始化表示式。這樣,當且僅當建構函式的初始化列表中不包含該資料成員時,這個資料成員就會自動使用初始化表示式進行初始化。

class

complex

complex

(float re)

:re_

(re)

,im_(0

)complex

(float re,

float im)

:re_

(re)

,im_

(im)

…private

:float re_;

float im_;

};

假設由於某種原因,我們不能使用預設引數來簡化建構函式,我們可以用什麼方式來優化上面這個**呢?使用資料成員的

預設初始化的話,我們就可以這麼寫:

class

complex

complex

(float re)

:re_

(re)

complex

(float re,

float im)

:re_

(re)

,im_

(im)

private

:float re_

;float im_;}

;

第乙個建構函式沒有任何初始化列表,所以類資料成員的初始化全部由預設初始化完成,re_ 和 im_ 都是 0。第二個建構函式提供了 re_ 的初始化,im_ 仍由預設初始化完成。第三個建構函式則完全不使用預設初始化。

C 11新特性之新型別與初始化

這是c 11新特性介紹,雖然現在來說有些不合時宜,但是也有一些有趣的地方。覺得麻煩的可以直接跳到最後哦!long long型別 c 11標準中新加入了long long型別屬性,占用空間不小於long型別。測試 如下 long large long max long long long large ...

C 11新特性 自動型別推斷和型別獲取

當編譯器能夠在乙個變數的宣告時候就推斷出它的型別,那麼你就能夠用auto關鍵字來作為他們的型別 cpp view plain copy auto x 1 編譯器當然知道x是integer型別的。所以你就不用int了。接觸過泛型程式設計或者api程式設計的人大概可以猜出自動型別推斷是做什麼用的了 幫你...

C 3 0新特性之物件初始化器

物件初始化器用於指定物件的乙個或多個可訪問的字段或屬性的值,通過 進行封閉起來,多個字段賦值之間通過逗號分割。具體語法元素是 1.物件建立表示式 new 型別 型別引數 可選 物件或集合初始化器可選 new 型別物件或集合初始化器 2.物件或集合初始化器 物件初始化器 集合初始化器 3.物件初始化器...