1. 程式人生 > >VSVC2010中常用的C++11特性

VSVC2010中常用的C++11特性

  1. static_assert 宣告

static_assert宣告在編譯時測試軟體斷言,這與在執行時進行測試的其他斷言機制不同。如果斷言失敗,則編譯也將失敗,且系統將發出指定的錯誤訊息。

const int nValue = 3;
static_assert(nValue < 10, "Error");

這樣編譯時就會報出Error的錯誤提示資訊。

  1. decltype作為操作符
    • 用於返回表示式的資料型別。
int Add(int a, int b)
{
    return a+b;
}
double dVal = 0.1;
const double dVal0 = 0.2;
const double& dVal1 = &dVal;
decltype(dVal) dVal2 = 0.3;     // dVal2是double型別
decltype(0.2) dVal3 = 0.2;      // dVal3是double型別
decltype(dVal1) dVal4 = &dVal0; //dVal4是const double&型別
decltype(Add(3, 4)) var = 4;  // var是函式返回的型別即int型別
decltype((dVal)) dVal5 = &dVal; //decltype((variable))多括號結果永遠是引用
int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
typedef int ArrType[5];
ArrType *Fun1(int i)
{
    return (i %2) ? &odd : &even;
}
decltype(odd) *Fun2(int i)
{
     return (i %2) ? &odd : &even;
}
 ArrType *arr1 =Fun1(1);
 ArrType *arr2 =Fun2(2);

Fun2使用decltype表示它的返回型別是個指標,並且該指標型別與odd型別一致。因為odd是陣列,所以

Fun2返回一個指定5個整數的陣列的指標。因為decltype(odd)型別的結果是陣列,所以如果想Fun2返回指標則必須在函式宣告時加上*符號。

    • 使用尾置返回型別

尾置返回型別(trailing return type)是在形參列表後面以->符號開始標明函式的返回型別,並在函式返回型別處用auto代替。尾置返回型別即可以直接指明型別,也可以用decltype推出出型別。形式:

auto Function(int i)->int
auto Fun3(int i)->int(*)[5]          // 返回指定陣列的指標
int n = 10;
auto Function(int i)->decltype(n)
template<class T, class W>
auto Function(T t, W w)->decltype(t+w)
{
return t +w;
}

// 如果是自定義型別,則應該過載+實現t+w

注:C++14中,已經將尾置返回型別去掉了,可以直接用auto推匯出型別。

  1. auto關鍵字

c++11修改了auto關鍵字的作用,auto型別說明符能讓編譯器替我們去分析表示式的初始化值來推匯出變數的型別。

  • 型別推導
    int j = 0;
    auto n = 0; // 0預設是int型別
    map<int,list<string>>::iteratori = m.begin();
    const int* const pInt =new int(2);
    auto *pAuto =pInt; // pAuto為int* cosnt型別,忽略頂層const保留底層const
    const auto& ref = 42;// 0K
    int Add(int a, intb){return a+b;}
    auto ret = Add(3,4); // ret是int
    auto m = j;   // m是int型別
    auto i = m.begin();
    *pAuto = 4;        // Error
    pAuto = new int(5);  // OK
  • auto和動態分配
  auto pInt1 = new int(4);
    int nValue = 4;
    auto pInt2 = newauto(nValue); //以nValue型別動態申請記憶體並以nValue賦值
    delete pInt2;
    // 以下是申請指向&nValue的指標
    auto** ppInt = newauto(&nValue);
    int** ppInt = new int*;
    *ppInt = &nValue;
    delete ppInt;

注:VS2010中對auto的動態分配支援有Bugdelete時會報錯,所以VS2010中不允許使用此功能。

  1. Lambda表示式

Lambda表示式就是匿名函式。Lambda表示式表示一個可呼叫的程式碼單元。與其他函式一樣,Lambda具有一個返回型別、一個引數列表和一個引數體。一個Lambda表示式具有如下形式:

[capture list](parameterlist))->return type { function body}

Capture list(捕獲列表)Lambda函式中定義的區域性變數列表,可以為空。

Parameter listfunction body和普通函式一樣。

return type,尾置型別指明,一般情況可以沒有,編譯器會自動推匯出返回型別。當函式體有多個返回值時,編譯會產生錯誤,需要指明返回型別。

string strRet = [](const string&str) { return "Hello from " + str; }("Lambda");
auto fun = [](const string&str)->string { return "Hello from " + str; };
strRet =fun("Lambda");

  Lambda表示式可以作為函式引數,例如在演算法函式中呼叫時:

int arr[10] = {0};
generate(arr,arr+10, []()->int { return rand() % 100; });

捕獲列表的使用,指出陣列第1個大於10的元素.

int nFlag = 10;
int nArr[] = {5,3, 2, 11, 4, 22};
auto first =find_if(nArr, nArr+6, [nFlag](int nValue){return nValue > nFlag;});
  1. 右值引用

為了支援移動語義,C++11引入了新的引用型別——左值引用(RValue Reference)。右值引用,即繫結到右值的引用,通過&&來獲取右值的引用。

左值:有具體的名字,作用域不止當前語句。

右值:匿名、作用域僅在當前語句。

C++11裡面對此作出的定義是:Things that aredeclared as rvalue reference can be lvalues or rvalues. The distinguishingcriterion is: if it has a name, then it is an lvalue. Otherwise, it is anrvalue.

普通型別的常量都是左值,但是字串常量因為生存週期是全域性的,所以字串常量是左值。

int&& nRRef = 1;

const string& strLRef = “LValue Reference”;

// nRRef雖然是左值引用,但它是具名的,所以nRRef是左值

右值引用:右值一旦離開當前語句,其生存期就會被銷燬。而右值引用則將右值的有效性移動到右值引用這個左值上。

通過右值引用的定義可以看出它的主要作用是將臨時變數的生存週期給轉移了,這樣就減少建立變數銷燬物件的損耗。

建構函式和賦值函式是建立物件最常用函式,也是右值引用發揮作用的地方。

移動建構函式和移動賦值函式

 class CMyString
    {
    public:
        CMyString()
        {
            m_data =NULL;
            m_len =0;
        }

        CMyString(const char* p)
        {
            m_len =strlen (p);
            Init(p);
        }

        CMyString(const CMyString&& str)
        {
            m_len =str.m_len;
            m_data =str.m_data;
            str.m_data =NULL;
            std::cout<< "Copy Constructor is called! source: " << m_data<< std::endl;
        }

        CMyString& operator=(const CMyString&& str)
        {
            if (this!= &str)
            {
                m_len =str.m_len;
                m_data =str.m_data;
                str.m_data= NULL;
            }
            std::cout<< "Copy Assignment is called! source: " << m_data<< std::endl;
            return*this;
        }

        virtual~CMyString()
        {
            if(m_data)
                delete[] m_data;
        }
    private:
        voidInit(const char *s)
        {
            m_data =new char[m_len+1];
            memcpy(m_data, s, m_len);
            m_data[m_len] = '\0';
        }

    private:
        char* m_data;
        size_t m_len;
    };

    CMyString GetMyString()
    {
        CMyString str= "abc";
        returnstr;    // A
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        CMyStringmyStr;
        myStr = GetMyString(); // B:1個右值賦給1個左值
        return 0;
    }

程式碼A返回1個無名的臨時物件,其實就是返回1個右值,這裡就會呼叫右值建構函式.程式碼B將返回的右值賦給左值,同樣呼叫右值賦值函式即移動賦值函式。

注:一旦資源完成移動(賦值)之後,源物件(即右值引用物件)不能再指向被移動的資源。正如上面的移動建構函式及移動賦值函式在完成指標轉移時,右值引用的指標必須指向NULL

標準庫函式 std::move

移動建構函式和移動賦值函式只接受右值作為引數,而所有的具名物件都是左值。那麼能不能將左值當作右值來使用呢?答案當然是可以的。標準庫提供了std::move這個函式,它完成的作用只是一個型別轉換,即將左值型別轉換成右值引用型別。move函式是通過模板實現的,VS2010程式碼如下:

// TEMPLATE _Remove_reference
template<class _Ty>
struct _Remove_reference
{    // removereference
typedef _Ty _Type;
};
 
template<class _Ty>
struct _Remove_reference<_Ty&>
{    // removereference
typedef _Ty _Type;
};
 
template<class _Ty>
struct _Remove_reference<_Ty&&>
{    // removervalue reference
typedef _Ty _Type;
};
// TEMPLATE FUNCTION move
template<class _Ty> inline
typenametr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{    // forward_Arg as movable
return ((typenametr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}

上面這段程式碼很簡單,卻有點難懂。其實這裡利用的是技術,也即利用了模板特化和偏特化。我們可以看一段簡單的示例來理解這段程式碼的功能(詳細見我之前文章)

template< typename T >
struct STRUCT_TYPE
{
    typedef intMY_TYPE;
    typedef__int64 POWER_TYPE;
};
template<>
struct STRUCT_TYPE<double>
{
    typedef floatMY_TYPE;
    typedefdouble POWER_TYPE;
};
template< typename T >
struct STRUCT_ALGO
{
   // 下面的Typename是指示T::MY_TYPE是一個型別而不是成員變數
    typedeftypename STRUCT_TYPE<T>::MY_TYPE myType;
    typedeftypename STRUCT_TYPE<T>::POWER_TYPE powType;
 
    powTypeGetPow(const myType& value)
    {
        returnvalue*value;
    }
};
<p lang="en-US" style="margin: 0in 0in 0in 0.375in; font-family: Calibri; font-size: 10.5pt;"> </p><p lang="en-US" style="margin: 0in 0in 0in 0.375in; font-family: Calibri; font-size: 10.5pt;">int _tmain(int argc, _TCHAR* argv[])</p><p lang="en-US" style="margin: 0in 0in 0in 0.375in; font-family: Calibri; font-size: 10.5pt;">{</p><p lang="en-US" style="margin: 0in 0in 0in 0.375in; font-family: Calibri; font-size: 10.5pt;">        __int64nPow = STRUCT_ALGO<int>::GetPow(4);</p><p lang="en-US" style="margin: 0in; font-family: Calibri; font-size: 10.5pt;">            double dPow =STRUCT_ALGO<double>::GetPow(5.0);</p><p lang="en-US" style="margin: 0in 0in 0in 0.75in; font-family: Calibri; font-size: 10.5pt;">return 0;</p><p lang="en-US" style="margin: 0in 0in 0in 0.75in; font-family: Calibri; font-size: 10.5pt;">}</p>

通過上面的程式碼可以看出,通過不同的模板形參,呼叫不同模板裡面的typedef型別,這樣可以達到不同型別模板形引數使用相同typedef名,共用程式碼。

我們再來看move函式的實現,其實也是一樣的。

一開始建立通脹模板_Remove_reference,然後利用模板偏特化,這裡只部分特化左值引用和右值引用特性。所以模板形參不能為空,如果是全特化的引數則省略。

首先了解下引用疊加的規則:

X& &X&& &X& &&均疊加成X&

X&& &&則疊加成X&&

string str1 = abc;

string&& str2 = std::move(string(”abc”));

string&& str3 = std::move(str1);

string&& str4 = std::move(str3);

string(abc)實參明顯是一個右值,則呼叫通用模板。

str11個左值,move(&&)函式則會將型別推導為左值引用。

str3則是1個右值引用,則move函式將會將其型別推導為右值引用。

_Remove_reference就達到去除引用的作用。

簡單理解就是move(&&)可以接受所有型別的引數,無論是左值、右值、左值引用、右值引用均可。

(typenametr1::_Remove_reference<_Ty>::_Type&&)則是去掉引用,然後強制型別轉換成右值引用。所有型別都可以強制轉換成右值引用。

 5. 標準庫函式  std::forward

有些函式需要將其實參連同型別不變地轉發給其他函式,包括實參的是否cosnt、

左值還是右值。函式std::forward就是為了完成這個功能而建立的。

forward在VS2010中的實現如下:

//TEMPLATE CLASS identity
template<class_Ty>
structidentity
{  // map _Ty to type unchanged
typedef_Ty type;
const_Ty& operator()(const _Ty& _Left) const
{  // apply identity operator to operand
return(_Left);
}
};
//TEMPLATE FUNCTION forward
template<class_Ty> inline
_Ty&&forward(typename identity<_Ty>::type& _Arg)
{  // forward _Arg, given explicitly specifiedtype parameter
return((_Ty&&)_Arg);
}

如果一個函式引數是指向模板型別引數的右值引用(如T&&),它對應的const屬性、左值、右值屬於將得到保持。

void Add(const int& i) 
{
cout << "inner(const X&)" << endl;
}
 
void Add(int& i) 
{
i++;
cout << "inner(const X&)" << endl;
}
 
void Add(int&& i) 
{
cout << "inner(X&&)" << endl;
}
 
template<typename T>
void AllAdd(T&& t)
{
   Add(t);                            // 呼叫1
   Add(forward<T>(t));    // 呼叫2
}
 
int_tmain(int argc, _TCHAR* argv[])
{
                  int nTemp = 3;
        const int nValue = nTemp;
       AddAll(nValue);  // 1, 2呼叫是相同的,均呼叫void Add(const int& i) 
       AddAll(nTemp);    // 1, 2呼叫是相同的,均呼叫void Add(int& i)
       AddAll(int(1));  // 1呼叫的是void Add(int& i),因為形參t有名字,是左值,故優先呼叫相應引用
      // 2呼叫void Add(int&& i),因為forward<T>能夠推匯出T是右值引用
 
        return 0;
}

通過上面的示例就能夠很好的理解std::forward所謂的完美轉發了,因為只要是通過T&&的形參,forward<T>都能夠返回它的實際型別.

相關推薦

VSVC2010常用C++11特性

static_assert 宣告 static_assert宣告在編譯時測試軟體斷言,這與在執行時進行測試的其他斷言機制不同。如果斷言失敗,則編譯也將失敗,且系統將發出指定的錯誤訊息。 const int nValue = 3; static_assert(nValue

《隨筆十一》—— C++的 “ C++ 11特性梳理 ”

  在面試中,經常被問的一個問題就是:你瞭解C++11哪些新特性?一般而言,回答以下四個方面就夠了: “語法糖”:nullptr, auto自動型別推導,範圍for迴圈,初始化列表, lambda表示式等 右值引用和移動語義 智慧指標 C++

VS2013C++11特性

轉自:http://www.admin10000.com/document/2504.html Visual C++ 2013 Preview 在6月釋出了,C++開發者又找到一個編譯器可以更好的支援ISO C++ 11 的特性了。本文介紹了這些新的特

C++ 11 特性的演算法

參考文獻:《C++ Primer》 一、algorithm介紹 algorithm譯為演算法,提供了大量基於迭代器的非成員模版函式。本文將盡可能全面的講解algorithm標頭檔案下的演算法以

C++11特性:列表初始化:VS2010vector的初始化方式

        在使用VS2010學習C++Primer第五版時,學習到了初始化的問題,算是一個小難點吧。         C++11的新特性支援列表初始化:將初始值放在花括號(注意不是圓括號)內進行

C++ 11特性

erro 匹配 編譯速度 void con 函數對象 roc _each 文件 1.auto:自動類型推導 編譯時對變量進行類型推導,不會對程序的運行效率造成影響; 不會影響編譯速度,因為編譯時也要右側推導,然後判斷與左側是否匹配; 通過初始化表達式

升級 gcc g++ 版本 支援-std=c++11 特性

CentOS 6.6 升級GCC G++ (當前最新GCC/G++版本為v6.1.0) 或者 新增yum 的 repo 檔案 也不行, 只能更新到 4.4.7! 採取手動編譯安裝了 1、 獲取安裝包並解壓 wget http://ftp.gnu.org/gnu/g

C++——VisualStudio的c++11特性(一)

前言 檢視官網https://msdn.microsoft.com/library/ 從VisualStudio2010講起,主要是針對c++11特性來研究vs開發c++的IDE的強大之處 VisualStudio2010下載release(釋出版本)地址:https://docs.mi

【程式設計技巧與C++11特性】總結

一,程式設計技巧 1.1排序效能問題 C ++的排序函式有兩種用法: 傳入一個functor物件; 直接傳入一個排序函式。 #include <iostream> #include <ctime> #include <algorithm&

c++ 11 特性之auto

 auto並沒有讓C++成為弱型別語言,也沒有弱化變數什麼,只是使用auto的時候,編譯器根據上下文情況,確定auto變數的真正型別。 auto在C++14中可以作為函式的返回值 #include<iostream> auto add(int x,int y) { &nb

[轉載]Visual Studio, GCC, Clang, Intel 對 C++11 特性的支援一覽表

參與翻譯(3人):throwable, jimmyjmh, Khiyuan距離我上次對C++11支援的不同編譯器的比較已經有大半年了。這次我來根據這些預覽版的編譯器的文件來看下它們是如何堆砌起來的。GCC的下個版本是4.8,以及Clang即將到來的版本是3.3 。如果你使用Visual Studio 2012

C語言】實際專案開發過程常用C語言函式的9大用法

C語言是當中最廣泛的計算機程式語言,是所有計算機程式語言的祖先,其他計算機程式語言包括當前流行的Java語言,都是用C語言實現的,C語言是程式設計效率最高的計算機語言,既能完成上層應用開發,也能完成底層硬體驅動程式設計,在計算機程式設計當中,特別是在底層硬體驅動開發當中,具有不可替代的作用。

c++11特性裡的多執行緒thread的用法

建立和啟動一條C++執行緒就像在C++原始碼中新增執行緒標頭檔案那麼簡便。我們來看看如何建立一個簡單的帶執行緒的HelloWorld: #include <iostream> #include <thread> using namespace std

在Eclipse實現C++ 11的完整支援

主要用來解決,Eclipse中編譯c++時,出現的std::threads,std::mutex等的unresolved問題 1、開啟Project -> Properties -> C/C++ General -> Path and Symbols -

如何寫cmake使其包含c++11特性 (-std=c++11如何寫進cmakeList.txt)

g++ 4.8.2 cmake 2.8 之前寫cmkae編譯帶有c++11特性的程式碼有這麼一句: set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 但是總會出現cc1plus: error: unrecognized

c++11特性之std::thread--初識二

上篇部落格《c++11特性之std::thread–初識》初步介紹了std::thread,並且介紹了幾個成員函式。 最後的一段程式碼留了點懸念,就是vs2015會報錯,錯誤如下: error

在安卓JNI/NDK使用C++11

在VS下編寫的程式移植到Eclipse下,出現了問題:       this file requires compiler and library support for iso C++ 2011 standard。 原因:是Eclipse使用的CDT預設未開啟C++11 

C++11特性--統一的初始化,std::initializer_list

        注:本系列筆記所有內容的參考資料來源於《C++ Primer Plus》             C++11特性需編譯器支援,如果是WindowS整合開發環境,一般需要在IDE的編譯器選項裡找或者工程的屬性裡可以配置"-std=c++11"             以devC++為例:proj

C++11特性--新的類功能--特殊的成員函式(移動建構函式,移動賦值運算子),預設方法和禁用方法(default,delete),委託建構函式,管理虛方法(override,final)

      class A      {         public:            void fun(int x )            {               cout<<x<<endl;             }                      

讓Dev支持C++11特性

好用 + - ++ 編譯器 alt 設置 可能 操作 選項 如果你使用的是 Dev-C++ ,忘記了在設置中包含 -std=c++11是無法讓Dev支持C++11特性的,導致一些C++11 中的好用的函數可能會產?生編譯?無法通過的情況。 這時候你需要