1. 程式人生 > >深入應用c++11讀書筆記--使用c++11讓程式更簡潔、更現代-1

深入應用c++11讀書筆記--使用c++11讓程式更簡潔、更現代-1

由來

    c++11由來已久,VS2015完全支援C++11標準,對C++14的支援已經基本完成,並支援部分C++17標準。但是,工作中使用到的語法卻還是以c++98/03為主,對新引入的語法知之勝少,之前也一直想做一些深入的瞭解,但一直苦於沒有一個良好的切入點,自從閱讀《深入應用c++11程式碼優化與工程級應用》後發現本書對於c++11的使用寫得相當透徹且配合例子很好理解,但粗看一遍往往不能深入領會其中的精髓,遂寫次筆記加強理解。

auto使用

    auto的使用可以說是我最先使用過的c++11特性了,沒有之一(主要是容器的遍歷用於標識迭代器變數)。

推導規則

    當時使用就如上所說的一個應用例子,也沒有細究auto推導的規則。書中做了總結,摘錄下:1.當不宣告為指標或引用時,auto的推導結果和初始化表示式拋棄引用和cv限定符(const和volatile限定符的統稱)後型別一致;2.當宣告為指標或引用時,auto的推導結果將保持初始化表示式的cv屬性。

auto的限制

1.auto不能用於函式引數: void func(auto a = 1) {} // error
2.auto不能用於非靜態成員變數:
c++
struct Foo
{
auto var1_ = 0;// error
static const auto var2_ = 0; // OK
}

3.auto 無法定義陣列,無法推匯出模板引數:
c++
int main()
{
int arr[10] = {0};
auto aa = arr;
auto rr[10] = arr; // error auto 無法定義陣列
Bar<int> bar;
Bar<auto> bb = bar; // error auto無法推匯出模板引數
return 0;
}

auto應用

1.用於容器遍歷時宣告迭代器型別;
2.用於在模板中接收一個模板函式的返回值用於後續處理。
c++
template<clas A>
void func()
{
auto val = A::get();
//...
}

decltype使用

    對於decltype和auto這兩個在應用上看著有些類似,有些情況下可以互換,auto主要根據變數的初始化表示式推到出邊個兩應該具有的型別,而若想要通過某個表示式得到型別,但不希望新變數和這個表示式具有同樣的值,此時auto就不適用了。此外,對於一般的標記符表示式,decltype將精確地推到出表示式定義本身的型別,不會像auto那樣在某些情況下捨棄掉引用和cv限定符。

decltype推導規則

    decltype推導規則大部分比較顯而易見,但一些情況還需要特別注意。
以下列出推導規則:decltype(exp)

  1. exp是識別符號、類訪問表示式–>decltype(exp)和exp型別一致;
  2. exp是函式呼叫–>decltype(exp)和返回值一致(函式返回值為純右值時,只有類型別可以攜帶cv限定符,此外則一般忽略cv限定);
  3. 其他情況,若exp是一個左值,則decltype(exp)是exp的左值引用,否則和exp型別一致。
        針對以上幾點進行舉例:
    針對2中忽略cv限定符的情況:
 const int func_cint(void);

int main()
{
    decltype(func_cint()) m = 0;
    return 0;
}

這裡寫圖片描述

針對3中描述的則需要比較注意,常常由於一個括號導致完全不同的推導:

struct Foo { int x; };

int main()
{
    const Foo foo = Foo();
    decltype(foo.x) a = 0;
    decltype((foo.x)) b = a; // 特別注意和上面的比較

    int n = 0, m = 0;
    decltype(m + n) c = 0;
    decltype(m += n) d = c;

    return 0;
}

附上vs上執行時的顯示,可以對照規則3進行理解:
這裡寫圖片描述

decltype應用

    書中有一步一步引出這種方式的步驟及和c++98/03的實現比較,這裡直接列出方案:

template<class ContainerT>
class Foo {
    decltype(ContainerT().begin()) it_;
public:
    void func(ConstinerT& container)
    {
        it_ = constiner.begin();
    }
    //...
}

auto與decltype結合使用

    這兩者結合主要用於返回型別後置語法,具體與c++98/03的比較就不羅列了,兩個裡字就行理解使用:

template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
    return t + u;
}
int& foo(int& i);
float foo(float& f);

template <typename T>
auto add(T &val) -> decltype(foo(val))
{
    return foo(val);
}

模板別名

c++11中新新增的using別名,絕大部分和typedef相同,這裡就舉個不同的例子:

template <typename val>
using str_map_t = std::map<std::string, val>;
//...
str_map_t<int> map1;

模板預設模板引數

    c++11中函式模板也支援預設引數,這個在之前c++98/03是不支援的。
注意:如果想禁止引數自動推導,可以使用外敷模板,示例如下

template <typename T>
struct identity
{
    typedef T type;
};

template<typename T = int>
void func(typename identity<T>::type val, T = 0) //  禁用了val的自動推導
{
    //...
}

列表初始化

具體就不多說了,舉幾個例子就可以知道用法了:

int a = {1}; // 雖然使用了等號,但它仍是列表初始化,私有的拷貝構造並不會影響它。
int a1{1};
int *a = new int{123};
int *arr = new int[3]{1, 2, 3};

如果自定義支援任意長度初始化列表,則需要使用到std::initializer_list,例子如下:

class Foo
{
public:
    Foo(std::initializer_list<int>) {

    }
};

Foo fool{ 1, 2, 3, 4 };

基於範圍的for迴圈

這個使用比較簡單,但有幾點需要注意:
1.for(auto n : arr) { //…} //注意這裡的arr只會計算一次,即首次即算出迴圈的終止條件,如果在迴圈中修改容器長度則迴圈次數仍然不會改變。
2.for迴圈可以直接遍歷陣列:

int fibarray[] = {0, 1, 1, 2, 3, 5, 8, 13};
for (auto v : fibarray) {
    std::cout << v << std::endl;
}