美文网首页
c++11/14/17新特性(1)

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

作者: Teech | 来源:发表于2021-10-24 15:34 被阅读0次

2.1auto关键字

auto expr;

  • 当expr包含cv描述符的时候,比如const int a = 1;auto b = a;此时b的类型为int,会丢失掉const类型
  • const int a[10]; auto b = a; 此时b类型退化成int*

作为对比

  • const int a= 1; auto &b = a; 此时b的类型为const int& 没有丢失const
  • int a[10]; auto& b = a; 此时b类型为int(&)[3]

尝试用auto申明一个变量的时候是申明一个新的值,语义是复制,所以会丢失掉cv标识符或者数组,如果加了引用后表示引用之前的变量,所以自然不会丢失ref类型,初始化表达式如果是数组或者函数,那么也同理退化成指针,如果加了引用自然不会退回。

auto 函数返回值推倒类型
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)

普通函数

auto equaltOne(int x){
if(x==1)
return true;
else
return false;
}
编译器要求如果一个函数有多个return语句的时候,需要推倒成相同的类型
否则会类似error: inconsistent deduction for auto return type: 'bool' and then 'int'

在new表达式

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;

还可以抽象出来用个模板函数来生成
template<typename MapType>
auto RevertMap(MapType somemap) {
    return std::map<typename decltype(somemap)::mapped_type,typename decltype(somemap)::key_type>();
}

decltype(expr) 返回一个表达式的类型

int x = 1;
//但是加括号多了引用类型
decltype((x)); //返回int&
//函数参数
struct A{};
A& fun();
decltype(func()); //A&

//++
int x=1;int y=2;
decltype(++x) //int& x的值并不会增加,因为编译器推倒的,而不是真的去执行
decltype(x+y) //int

注意 decltype(expr) 表达式并不会真的会执行,因为编译器推倒类型的,并不会真的被执行、

auto和decltype的区别个人理解是decltype可以通过一个表达式来获取类型,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 移动语义

struct MemoryBlock {
    int *_data;
    int _len;
    MemoryBlock(int l);
    //复制构造函数
    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);//复制构造函数
//delete mb1
//这个例子浪费了一个复制构造 一个析构

push back会resize,resize的过程中会先析构掉原来的item,然后在复制过来
这个背景下,所以复制构造在这里会变得不可忍受,还有有的对象不支持复制比如锁,不能被其他对象复制。

想实现移动语义,class要支持move语义,且需要指示编译器生成代码时调用定义的move操作

想支持移动构造的class T 需要满足以下特点

  1. 不能是个模板构造函数
  2. 第一个参数 T&&,const T&&,volatile T&& 或者const volatile T&&
  3. 剩下的参数都有默认值
class SomeClass{
    SomeClass([CV] SomeClass&&);                 //是
    SomeClass([CV] SomeClass&&,int parm1=1,...); //是
    template<typename U> SomeClass(U&&); //not move actor
    
}

接着上述的MemoryBlock

//移动构造函数
MemoryBlock(MemoryBlock&*other):_data(NULL),_len(0){
    _data = other._data;
    _len = other._len;
    other._data = NULL;
}

3.2指示编译器来调用移动构造

std::vector<MemoryBlock> vec;
MemoryBlock mb1(10);
vec.push_back(mb1); //copy ctor or move ctor 都支持 编译器怎么选呢?

3.2 value category

c++ 表达式通过2个部分的属性 一个type 和 一个value category

  • LValue
  • Prvalue
  • Xvalue

常见的lvalue表达式

  • 变量,数据成员,函数
  • 如果函数返回一个lvalue引用,那么也是个左值 int& f(),f();++it
  • a.m,a->p 当a是个lvalue
    通俗的说可以取地址的就是左值

prvalue

  • 字面值 42 true or null
  • 如果函数调用,返回的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

  • 函数返回的是rvalue reference ,int&& f(); f()表达式
  • static_cast<char&&>(x)
  • a[n],a.m 当a是rvalue

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

用法介绍

  • 可以使用rvalue绑定到cosnt lvalue reference上 const int&i=1;
  • 可以绑定到右值引用 int&& i = 5;(新标准)

函数重载时,如果参数有const lvalue reference 和 rvalue重载,如果参数为rvalue那么会调用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;
//这里需要注意 表达式int&& x是个rvalue 但是x变量是个lvalue
//本质变量是个左值
f(x);       //lvalue

如果使用xvalue是构造一个对象,那么这个函数的move 语义版本的函数会被调用

  • T a = static_cast<T&&>(b) 或者T a(static_cast<T&&>(b)) 移动构造/赋值函数
  • f(static_cast<T&&>(a)); f是void f(T)

继续回到之前谈的怎么指示编译器调用move语义的函数

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

//只有xvalue才会调用到move语义的函数,prvalue不会调用到move语义函数
vec.push_back(static_cast<MemoryBlock&&>(mb1));

std::move 本质就是static_cast<MemoryBlock&&> 转换成rvalue

4.完美转发

4.1 forwarding problem

void g(const U&);   //复制语义
void g(U&&);        //移动语义
void wrapper(U&t){
    g(t);
}

void wrapper(U&&t){
    //这里虽然U&&t是xvalue 但是 t是个变量 所以t是lvalue
    //所以这里要强制转换下
    g(static_cast<U&&>(t));
}

4.1 forwarding reference

转发引用是个特殊的引用

  • 可以绑定到lvalue
  • 可以绑定到rvalue
  1. 函数参数为模块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 不是个转发引用因为有c描述符,只能绑定到rvalue
U&& u 是个转发引用

//例子3 
void wrapper(U&&t){
    这里怎么使用呢?
    g(?t);
}
可以使用新的标准库的g(std::forward<T>(t))
不论参数传入lvalue,rvalue都会保留,所以才叫“完美转发”

4.3 std::forward

怎么实现这个模板

  1. 模板参数推倒规则Deduction
  2. 引用折叠
template<class T>
int f(T&& x){}
f(arg)
当arg是lvalue,x被推倒成引用类型(T&)

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

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

4.3 引用折叠

引用的应用在模板的类型推倒或者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>& 这个要保证你传入进来的是个lvalue
static_cast<T&&> 强制转换rvalue-ref 类型

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

同理rvalue

总结:

  • forwarding ref
  • std::forward 实现

统一的初始化语法

5.1 初始化

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

int a(1);//初始化
int b();//函数申明
int c(foo);//歧义
98标准
string a = {"foo","bar"};

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

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

int a(1);//初始化
int b();//函数申明
int c(foo);//歧义

c++17标准
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};//初始化

新标准中,任意的原来初始化小括弧的地方,都可以用大括弧来表示


struct S{
    //构造函数1
    S(std::initializer_list<int> l);
    //构造函数2
    s(int t);
}

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

S s{1}; //这个会调用到构造函数1
//如果想调用到版本2
S s(1);
//这个应该是统一初始化列表函数唯一的需要注意的地方
//std::initializer_list<int>内部是 array of const T

5.2 统一初始化

  1. {} 如果信息丢失 不接受隐式转换
    • int i = {1.5};//errror
    • char c = {12}; //ok
  2. {}不是表达式 所以没有类型,模板中不能当类型推倒

6 class future

6.1 delete,default

class X{
    X& operator=(const X&) = delete;//禁用复制
    X(const X&) = delete; //禁用拷贝构造
}

class X{
    X& operator=(const X&) = default;//使用编译器生成的默认版本函数
    X(const X&) = default; 
}

6.2 override,final

override 派生类中显式申明要覆盖基类的函数
final 基类中显式申明 不能被覆盖

struct B {
    virtual void f() const final;
    //final修饰函数后 表示不能被派生类覆盖这个函数
    //const修饰后 标识成员函数不会修改成员变量
}

6.3 构造函数delegating

class Foo{
    public:
        Foo(char x,int y){}
        //构造函数代理,只能有代理函数 不能继续初始化其他成员变量
        Foo(int y): Foo('a',y){}
        
        Foo(int y): Foo('a',y),other_int(9){}类似这样的 是错的
        
}

6.4 默认成员初始化

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

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

6.5 继承构造函数

struct B{
    void f(double x){}
};
struct D : public B{
    //导入所有的 B::f()s 到当前命名空间
    using B::f;
    //添加新的f版本函数
    void f(int x){}
};

7 总结

  • 类型推倒
  • 移动语义
  • 完美转发
  • 统一初始化列表
  • class的新特性

相关文章

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

    2.1auto关键字 auto expr; 当expr包含cv描述符的时候,比如const int a = 1;a...

  • C++17新特性

    程序喵之前已经介绍过C++11的新特性和C++14的新特性,链接如下:xxx,今天向亲爱的读者们介绍下C++17的...

  • 记一次macOS Mojave升级GCC

    目录 前言 安装GCC 最后 前言 最近迷上了泛型编程, 看到了C++11, 14, 17的很多酷炫新特性. 之前...

  • C++11/14/17新特性

    C++11/14/17常用特性 关键字 auto 让编译器根据上下文情况,确定auto变量的真正类型,可以作为函数...

  • 开篇第一章--开发环境

    1. Boost简介 Boost 是一款C++准标准库,其好多特性都被C++11/14/17标准所引用。Boost...

  • C++14新特性的所有知识点全在这儿啦!

    前面程序喵介绍过C++11的新特性,在这里(),这篇文章介绍下C++14的新特性。 函数返回值类型推导 C++14...

  • c++11/14/17新特性(2)

    constexpr 优化方式 动态规划 可以做到O(N)的算法,如果想做到O(1),那么可以把计算消耗挪到编译期间...

  • 【Effective Modern C++】索引

    本书讲述了C++11/14新特性的用法和原理。1. 类型推断01 理解模板类型推断02 理解auto类型推断03 ...

  • 阿里巴巴面试题基础篇 C++11

    ● 请问C++11有哪些新特性? 参考回答: C++11 最常用的新特性如下: auto关键字:编译器可以根据初始...

  • C++11/14新特性

    1.nullptr nullptr 出现的目的是为了替代 NULL,传统 C++ 会把 NULL、0 视为同一种东...

网友评论

      本文标题:c++11/14/17新特性(1)

      本文链接:https://www.haomeiwen.com/subject/ivgraltx.html