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)
- 不能是個(gè)模板構(gòu)造函數(shù)
- 第一個(gè)參數(shù) T&&胖烛,const T&&,volatile T&& 或者const volatile T&&
- 剩下的參數(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
- 函數(shù)參數(shù)為模塊T
- &&
- 沒有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è)模板
- 模板參數(shù)推倒規(guī)則Deduction
- 引用折疊
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)一初始化
- {} 如果信息丟失 不接受隱式轉(zhuǎn)換
- int i = {1.5};//errror
- char c = {12}; //ok
- {}不是表達(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的新特性