c++11/14/17新特性(1)

2.1auto關(guān)鍵字

auto expr;

  • 當(dāng)expr包含cv描述符的時(shí)候粤铭,比如const int a = 1;auto b = a;此時(shí)b的類型為int,會丟失掉const類型
  • const int a[10]; auto b = a; 此時(shí)b類型退化成int*

作為對比

  • const int a= 1; auto &b = a; 此時(shí)b的類型為const int& 沒有丟失const
  • int a[10]; auto& b = a; 此時(shí)b類型為int(&)[3]

嘗試用auto申明一個(gè)變量的時(shí)候是申明一個(gè)新的值,語義是復(fù)制,所以會丟失掉cv標(biāo)識符或者數(shù)組苟呐,如果加了引用后表示引用之前的變量汪厨,所以自然不會丟失ref類型,初始化表達(dá)式如果是數(shù)組或者函數(shù)蜂厅,那么也同理退化成指針匪凡,如果加了引用自然不會退回。

auto 函數(shù)返回值推倒類型
template<class T,class U>
? add(T t,U u) { return a + b}

可以這樣
template<class T,class U>
auto add(T t,U u) { return a + b} auto類型為operator+(T,U)

普通函數(shù)

auto equaltOne(int x){
if(x==1)
return true;
else
return false;
}
編譯器要求如果一個(gè)函數(shù)有多個(gè)return語句的時(shí)候掘猿,需要推倒成相同的類型
否則會類似error: inconsistent deduction for auto return type: 'bool' and then 'int'

在new表達(dá)式

auto p = new auto('c') // p is a char*

2.2decltype

-例子1
std::map<char,int> mymap;
mymap.insert(std::pair<char,int>('a',100));
mymap.insert(std::map<char,int>::value_type('z',101));

--上樹兩種寫法hard code map的value type的具體類型
--這種寫法不依賴mymap的具體類型
mymap.insert(decltype(mymap)::value_type('z',101))

-例子2
std::map<int,std::string> somemap;
std::map<std::string,int> remap;
想通過上述的map來獲取下面的map病游,顛倒key和value的類型
也可以這么寫
std::map<typename decltype(somemap)::mapped_type,typename decltype(somemap)::key_type> remap;

還可以抽象出來用個(gè)模板函數(shù)來生成
template<typename MapType>
auto RevertMap(MapType somemap) {
    return std::map<typename decltype(somemap)::mapped_type,typename decltype(somemap)::key_type>();
}

decltype(expr) 返回一個(gè)表達(dá)式的類型

int x = 1;
//但是加括號多了引用類型
decltype((x)); //返回int&
//函數(shù)參數(shù)
struct A{};
A& fun();
decltype(func()); //A&

//++
int x=1;int y=2;
decltype(++x) //int& x的值并不會增加,因?yàn)榫幾g器推倒的稠通,而不是真的去執(zhí)行
decltype(x+y) //int

注意 decltype(expr) 表達(dá)式并不會真的會執(zhí)行衬衬,因?yàn)榫幾g器推倒類型的,并不會真的被執(zhí)行改橘、

auto和decltype的區(qū)別個(gè)人理解是decltype可以通過一個(gè)表達(dá)式來獲取類型滋尉,auto是直接推倒

2.3 decltype(auto)

//例子1
auto f(){
    return g();
}
//g()可能返回T或者T&,由于aotu會去除cv描述符飞主,所以退化成T

//例子2
//想要保留引用特性 所以得這么做
decltype(auto) f(){
    return g();
}
//例子3
int x = 1;
int& rx = x;
auto rx1 = rx;              //int
decltype(auto) rx2 = rx;    //int&

3.1 move sementic 移動(dòng)語義

struct MemoryBlock {
    int *_data;
    int _len;
    MemoryBlock(int l);
    //復(fù)制構(gòu)造函數(shù)
    MemoryBlock(const MemoryBlock&other):_data(NULL),_len(0){
        _data = new int(other._len);
        _len = other._len;
        std::memcpy(_data,other._data,_len * sizeof(_len));
    }
};

用法1

std::vector<MemoryBlock> vec;
MemoryBlock mb1(10);
vec.push_back(mb1);//復(fù)制構(gòu)造函數(shù)
//delete mb1
//這個(gè)例子浪費(fèi)了一個(gè)復(fù)制構(gòu)造 一個(gè)析構(gòu)

push back會resize狮惜,resize的過程中會先析構(gòu)掉原來的item,然后在復(fù)制過來
這個(gè)背景下碌识,所以復(fù)制構(gòu)造在這里會變得不可忍受碾篡,還有有的對象不支持復(fù)制比如鎖,不能被其他對象復(fù)制筏餐。

想實(shí)現(xiàn)移動(dòng)語義开泽,class要支持move語義,且需要指示編譯器生成代碼時(shí)調(diào)用定義的move操作

想支持移動(dòng)構(gòu)造的class T 需要滿足以下特點(diǎn)

  1. 不能是個(gè)模板構(gòu)造函數(shù)
  2. 第一個(gè)參數(shù) T&&胖烛,const T&&,volatile T&& 或者const volatile T&&
  3. 剩下的參數(shù)都有默認(rèn)值
class SomeClass{
    SomeClass([CV] SomeClass&&);                 //是
    SomeClass([CV] SomeClass&&,int parm1=1,...); //是
    template<typename U> SomeClass(U&&); //not move actor
    
}

接著上述的MemoryBlock

//移動(dòng)構(gòu)造函數(shù)
MemoryBlock(MemoryBlock&*other):_data(NULL),_len(0){
    _data = other._data;
    _len = other._len;
    other._data = NULL;
}

3.2指示編譯器來調(diào)用移動(dòng)構(gòu)造

std::vector<MemoryBlock> vec;
MemoryBlock mb1(10);
vec.push_back(mb1); //copy ctor or move ctor 都支持 編譯器怎么選呢眼姐?

3.2 value category

c++ 表達(dá)式通過2個(gè)部分的屬性 一個(gè)type 和 一個(gè)value category

  • LValue
  • Prvalue
  • Xvalue

常見的lvalue表達(dá)式

  • 變量,數(shù)據(jù)成員佩番,函數(shù)
  • 如果函數(shù)返回一個(gè)lvalue引用众旗,那么也是個(gè)左值 int& f(),f();++it
  • a.m,a->p 當(dāng)a是個(gè)lvalue
    通俗的說可以取地址的就是左值

prvalue

  • 字面值 42 true or null
  • 如果函數(shù)調(diào)用,返回的none-reference趟畏,str1+str2,it++,a+b
  • &a
  • cast expression 到none -reference類型 static_cast<double>(x)
  • this 指針

xvalue
rvalue:prvalue + xvalue
rvalue reference: reference to rvalue. 比如&&

xvalue

  • 函數(shù)返回的是rvalue reference ,int&& f(); f()表達(dá)式
  • static_cast<char&&>(x)
  • a[n],a.m 當(dāng)a是rvalue

不能對rvalue取地址 &i++[3],&23
不能放在等號右邊 static_cast<char&&>(x) = 'a'

用法介紹

  • 可以使用rvalue綁定到cosnt lvalue reference上 const int&i=1;
  • 可以綁定到右值引用 int&& i = 5;(新標(biāo)準(zhǔn))

函數(shù)重載時(shí)贡歧,如果參數(shù)有const lvalue reference 和 rvalue重載,如果參數(shù)為rvalue那么會調(diào)用rvalue的重載

void f(int &x){
    std::cout<<"lvalue reference overload"<<std::endl;
}
void f(const int &x){
    std::cout<<"lvalue reference to const overload"<<std::endl;
}
void f(int &&x){
    std::cout<<"rvalue reference overload"<<std::endl;
}
int i=1;
const int ci = 2;
f(i);   //lvalue ref 
f(ci);  //lvalue ref to const 
f(3);   //rvalue
f(static_cast<int&&>(i));   //rvalue


int&& x = 1;
//這里需要注意 表達(dá)式int&& x是個(gè)rvalue 但是x變量是個(gè)lvalue
//本質(zhì)變量是個(gè)左值
f(x);       //lvalue

如果使用xvalue是構(gòu)造一個(gè)對象,那么這個(gè)函數(shù)的move 語義版本的函數(shù)會被調(diào)用

  • T a = static_cast<T&&>(b) 或者T a(static_cast<T&&>(b)) 移動(dòng)構(gòu)造/賦值函數(shù)
  • f(static_cast<T&&>(a)); f是void f(T)

繼續(xù)回到之前談的怎么指示編譯器調(diào)用move語義的函數(shù)

std::vector<MemoryBlock> vec;
MemoryBlock mb1(10);
vec.push_back(mb1); //copy ctor lvalue

//只有xvalue才會調(diào)用到move語義的函數(shù)利朵,prvalue不會調(diào)用到move語義函數(shù)
vec.push_back(static_cast<MemoryBlock&&>(mb1));

std::move 本質(zhì)就是static_cast<MemoryBlock&&> 轉(zhuǎn)換成rvalue

4.完美轉(zhuǎn)發(fā)

4.1 forwarding problem

void g(const U&);   //復(fù)制語義
void g(U&&);        //移動(dòng)語義
void wrapper(U&t){
    g(t);
}

void wrapper(U&&t){
    //這里雖然U&&t是xvalue 但是 t是個(gè)變量 所以t是lvalue
    //所以這里要強(qiáng)制轉(zhuǎn)換下
    g(static_cast<U&&>(t));
}

4.1 forwarding reference

轉(zhuǎn)發(fā)引用是個(gè)特殊的引用

  • 可以綁定到lvalue
  • 可以綁定到rvalue
  1. 函數(shù)參數(shù)為模塊T
  2. &&
  3. 沒有cv描述符
//例子
template<class T>
int g(T&& x);

//例子2
template<class T,class U>
int g(const T&&x,U&& u);

const T&&x 不是個(gè)轉(zhuǎn)發(fā)引用因?yàn)橛衏描述符律想,只能綁定到rvalue
U&& u 是個(gè)轉(zhuǎn)發(fā)引用

//例子3 
void wrapper(U&&t){
    這里怎么使用呢?
    g(绍弟?t);
}
可以使用新的標(biāo)準(zhǔn)庫的g(std::forward<T>(t))
不論參數(shù)傳入lvalue,rvalue都會保留技即,所以才叫“完美轉(zhuǎn)發(fā)”

4.3 std::forward

怎么實(shí)現(xiàn)這個(gè)模板

  1. 模板參數(shù)推倒規(guī)則Deduction
  2. 引用折疊
template<class T>
int f(T&& x){}
f(arg)
當(dāng)arg是lvalue,x被推倒成引用類型(T&)

//這里被推倒成引用類型
int i;
f(i); ==> f<int&>(int&)

f(0); ==> f<int>(int&&)

4.3 引用折疊

引用的應(yīng)用在模板的類型推倒或者typedef推倒中

  • && + && -> &&
  • 其他,-> &
//typedef 類型推倒
typedef int& lref;
typedef int&& rref;
int n;

lref& r1 = n;//lref& --> int& & -> int&

lref&& r2 = n;//lref&& --> int& && -> int&

rref& r3 = n;//rref& --> int&& & -> int&

rref& r4 = n;//rref&& --> int&& && -> int&&

模板類型推倒

template<class T>
void wrapper<T&& t>{
    g(std::forward<T>(t));
}

template<class T>
T&& forward(std::remove_reference_t<T>& v){
    return static_cast<T&&>(v);
}
std::remove_reference_t<T>& 這個(gè)要保證你傳入進(jìn)來的是個(gè)lvalue
static_cast<T&&> 強(qiáng)制轉(zhuǎn)換rvalue-ref 類型

//例子
wrapper(arg);
如果arg是int類型
1. wrapper<T&& t> -> T被推倒成int&
2. T是int&,T&& -> int& && -> int&
所以如果傳入的lvalue 那么結(jié)果得到的就是lvalue

同理rvalue

總結(jié):

  • forwarding ref
  • std::forward 實(shí)現(xiàn)

統(tǒng)一的初始化語法

5.1 初始化

  • 值初始化 std::string s();
  • 直接初始化 std::string s("hello")
  • 復(fù)制初始化 std::string s = "hello"
  • 列表初始化 std::string s{'a','b','c'}
  • aggregate 初始化 char a[3] = {'a','b','c'}
  • 引用初始化 char& c = a[0]
  • 默認(rèn)初始化 std::string s;
string a[] = {"foo","bar"}
f({"foo","bar"}) //error 
vector<string> v = {"foo","bar"} //error

int a(1);//初始化
int b();//函數(shù)申明
int c(foo);//歧義
98標(biāo)準(zhǔn)
string a = {"foo","bar"};

void f(string a[]);
f({"foo","bar"});//error

vector<string> v = {"foo","bar"} //error

int a(1);//初始化
int b();//函數(shù)申明
int c(foo);//歧義

c++17標(biāo)準(zhǔn)
string a = {"foo","bar"};

void f(string a[]);
f({"foo","bar"});//right

vector<string> v = {"foo","bar"} //right

int a{1};//初始化
int b{1};//初始化
int c{1};//初始化

新標(biāo)準(zhǔn)中樟遣,任意的原來初始化小括弧的地方而叼,都可以用大括弧來表示


struct S{
    //構(gòu)造函數(shù)1
    S(std::initializer_list<int> l);
    //構(gòu)造函數(shù)2
    s(int t);
}

S s = {1,2,3,4,5}

S s{1}; //這個(gè)會調(diào)用到構(gòu)造函數(shù)1
//如果想調(diào)用到版本2
S s(1);
//這個(gè)應(yīng)該是統(tǒng)一初始化列表函數(shù)唯一的需要注意的地方
//std::initializer_list<int>內(nèi)部是 array of const T

5.2 統(tǒng)一初始化

  1. {} 如果信息丟失 不接受隱式轉(zhuǎn)換
    • int i = {1.5};//errror
    • char c = {12}; //ok
  2. {}不是表達(dá)式 所以沒有類型,模板中不能當(dāng)類型推倒

6 class future

6.1 delete,default

class X{
    X& operator=(const X&) = delete;//禁用復(fù)制
    X(const X&) = delete; //禁用拷貝構(gòu)造
}

class X{
    X& operator=(const X&) = default;//使用編譯器生成的默認(rèn)版本函數(shù)
    X(const X&) = default; 
}

6.2 override豹悬,final

override 派生類中顯式申明要覆蓋基類的函數(shù)
final 基類中顯式申明 不能被覆蓋

struct B {
    virtual void f() const final;
    //final修飾函數(shù)后 表示不能被派生類覆蓋這個(gè)函數(shù)
    //const修飾后 標(biāo)識成員函數(shù)不會修改成員變量
}

6.3 構(gòu)造函數(shù)delegating

class Foo{
    public:
        Foo(char x,int y){}
        //構(gòu)造函數(shù)代理葵陵,只能有代理函數(shù) 不能繼續(xù)初始化其他成員變量
        Foo(int y): Foo('a',y){}
        
        Foo(int y): Foo('a',y),other_int(9){}類似這樣的 是錯(cuò)的
        
}

6.4 默認(rèn)成員初始化

int x = 0;
struct Foo{
    inline static s = 1;
    int n = ++x;
    Foo(){}   //默認(rèn)成員初始化
    Foo(int arg): n(arg){}//成員初始化
}

Foo f{10};
std::cout<<x<<std::endl; //0
Foo f1{};
std::cout<<x<<std::endl;//1

6.5 繼承構(gòu)造函數(shù)

struct B{
    void f(double x){}
};
struct D : public B{
    //導(dǎo)入所有的 B::f()s 到當(dāng)前命名空間
    using B::f;
    //添加新的f版本函數(shù)
    void f(int x){}
};

7 總結(jié)

  • 類型推倒
  • 移動(dòng)語義
  • 完美轉(zhuǎn)發(fā)
  • 統(tǒng)一初始化列表
  • class的新特性
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瞻佛,隨后出現(xiàn)的幾起案子脱篙,更是在濱河造成了極大的恐慌,老刑警劉巖伤柄,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绊困,死亡現(xiàn)場離奇詭異,居然都是意外死亡响迂,警方通過查閱死者的電腦和手機(jī)考抄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門细疚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔗彤,“玉大人,你說我怎么就攤上這事疯兼∪欢簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵吧彪,是天一觀的道長待侵。 經(jīng)常有香客問我,道長姨裸,這世上最難降的妖魔是什么秧倾? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮傀缩,結(jié)果婚禮上那先,老公的妹妹穿的比我還像新娘。我一直安慰自己赡艰,他們只是感情好售淡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般揖闸。 火紅的嫁衣襯著肌膚如雪揍堕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天汤纸,我揣著相機(jī)與錄音衩茸,去河邊找鬼。 笑死贮泞,一個(gè)胖子當(dāng)著我的面吹牛递瑰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隙畜,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抖部,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了议惰?” 一聲冷哼從身側(cè)響起慎颗,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎言询,沒想到半個(gè)月后俯萎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡运杭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年夫啊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辆憔。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撇眯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虱咧,到底是詐尸還是另有隱情熊榛,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布腕巡,位于F島的核電站玄坦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绘沉。R本人自食惡果不足惜煎楣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望车伞。 院中可真熱鬧择懂,春花似錦、人聲如沸帖世。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赂弓,卻和暖如春绑榴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盈魁。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工翔怎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杨耙。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓赤套,卻偏偏與公主長得像,于是被迫代替她去往敵國和親珊膜。 傳聞我的和親對象是個(gè)殘疾皇子容握,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容