c++2.0 新特性(c++11/c++14)

c++ 2.0新特征 c++11/14

语言新特性

1、variadic template 变参模板

template <typename T, typename... Types>  // ...为模板参数包
// sizeof...(args)获取参数个数
void printX(const T& firstArg, const Types&... args) {  // ...函数参数类型包
   cout << firstArg << endl;
   printX(args...);        // ...函数参数包
}

void printX() {
   
}

// 使用方法
printX(7,5, "hello", bitset<16>(377), 42);

 

2、nullptr

解决重载问题

void f(int);
void f(void*);

// 使用
f(0);  // f(int);
f(NULL);  // 无法确定调用哪个
f(nullptr);   // f(void*)

 

3、auto与deltype

auto:

  • 根据初始值推断类型,所以必须初始化

  • 忽略顶层const、忽略引用

  • 实际执行语句or表达式

deltype:

  • 根据deltype()里面的表达式推断、不执行语句、无需初始化

  • 保留所有的属性如const、引用

  • deltype(())为引用类型

  1. 函数返回值

// 模板
template <typename T1, typename T2>
deltype(x+y) add(T1 x, T2 y);
// lambda
[...](...) mutable throwSepc -> retType {}
  1. 变量声明、特别是模板元编程

  2. 传递lambda变大之的类型

auto cmp = [](const Person& p1, const Person& p2) {...};

std::set<Person, deltype(cmp)> coll(cmp);

 

4、初始化列表

vector<int> v{1,2,3};
vector<string> v2{"hello", world}

// 原理为:编译器看到{t1,t2...tn}会做出一个initializer_list<T>,并关联到array<T, b>
// 调用函数f(函数f一般为构造函数)时将array内的元素逐一传给函数
// 但是如果函数f接受的参数为initializer_list<T>,则直接传入无需一一传递

// initializer_list<T>
// 初始化操作
int i;  // i没有初值
int j{}; // j=0
int* p;  // p没有初值
int* q{};  // q=nullptr;
// 强类型检查
int x = {5.3}  // error、gcc为warning
int x = (5.3)  // x = 5;
// 自己使用
class P {
   public:
  P(int a, int b) {
           cout << a << " " << b <<endl;
      }
  // initializer_list可以认为是容器
  p(initializer_list<int> initlist) {
           for (auto i : initlist) {
               cout << i << " ";
          }
           cout << endl;
      }
}
// 底层原理
template <typename E>
class initializer_list {
   public:
   typedef E value_type;
   typedef const E& reference;
   typedef const E& const_reference;
   typedef size_t size_type;
   typedef const E* iterator;
   typedef const E* const_iterator;
   
   private:
   iterator _M_array;  // 数组头部
   size_type _M_len;   // 长度
   // 编译器可以调用私有的构造函数
   constexpr initializer_list(const_iterator a, int size):
  _M_array(a), _M_len(size) {};
   
   public:
   constexpr initializer_list() noexcept
      : M_array(0), _M_len(0) {}
   
}

constexpr:

https://blog.csdn.net/weixin_40087851/article/details/82754189

 

5、explicit

主要用来构造函数、去除掉隐形构造

class P {
   public:
  explicit P(int a, int b) {
           cout << a << " " << b <<endl;
      }
  // initializer_list可以认为是容器
  p(initializer_list<int> initlist) {
           for (auto i : initlist) {
               cout << i << " ";
          }
           cout << endl;
      }
}

P p1 = {77, 5};  // 调用的是P(a, b),而不是初始化列表,由于执行explicit,因此报错

 

6、for循环

编译器进行替换

for (auto& e : vc) {
   //
   do_something();
}

 

7、=default、=delete

=default:让编译器生成Big-Five

=delete:不允许使用某个函数

 

8、alias template

template <typename T>
using Vec = std::vector<T, Alloc<T>>;   // 不能对alias template做偏特化或者全特化

//使用
Vec<int> coll -->  vector<int, Alloc<int>> coll;

// 可否使用define or typedef
#define Vec<T> template<typename T> std::vector<T, Alloc<T>>;
Vec<int> coll --> template<typename int> std::vector<int, Alloc<int>> coll;   // 这不是我们想要的
// typedef 是不接受参数的
typedef std::vector<int, Alloc<int>> Vec;  // 只能使用int, 也不是我们想要的
// 难道只是为了少打几个字?
// 自然不是,可以实现之前无法实现的函数接口
// 详情看下节

 

9、template template parameter

template <typename T,
template<class> class Container>
class XCIs {
   private:
  Container<T> c;
  public:
  XCIs() {
           for (long i = 0; i < SIZE; ++i)
          c.insert(c.end(), T);
        Container<T> c1(c);
           Container<T> c2(std::move(c));
      }
}
// 使用
XCIs<MyString, vector> c1;
// 会报错,为何?
// 模板模板参数不能使用默认值
// 解决方案如下:
template<typename T>
using Vec = vector<T, alloc<T>>;

XCIs<MyString, Vec> c1;

 

10、type alias(similar to typedef)

// typedef void(*func)(int, int)
using func = void(*)(int, int);

 

11、noexcept

1、当使用noexcept时,std::teminate()函数会被立即调用,而不是调用std::unexpected(); 因此,在异常处理的过程中,编译器不会回退栈,这为编译器的优化提供了更大的空间

// 用法:声明函数不抛出异常
// 第一种
void foo() noexcept;  ->  void foo() noexcept(true);
// 第二种 带条件
void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y))) {
   x.swap(y);
}

2、非常重要:

  • 移动构造函数必须用noexcept声明、特别是vector

  • 只有这样,vector和deque在扩容过程中,才会使用移动构造函数,否则调用拷贝构造函数

 

12、override

虚函数重写,记得与重载overload区分开

 

13、final

不允许被重写

// 1、函数中使用
struct Base {
   virtual void f() final;
}
struct Devide {
   void f() {
       // 会报错
  }
}
// 类使用
struct Base final {};
struct Devide : public Base {}; // 会报错

 

标准库新特性

 

1、lambda表达式

  • 可以用来当做参数或者object,匿名的function object

  • 没有默认构造函数、也没用赋值操作符

格式为:

[...](...) mutable throwSpec -> retType {}
// 赋值
[=]
// 引用
[&]
// 使用1
int id = 0;
auto f = [id]() mutable {
   cout << id << endl;
   ++id;
}
id = 42;
f();  // PS: 0
f();  // PS: 1
f();  // PS: 2

// 使用2
int id = 0;
auto f = [&id]() {
   cout << id << endl;
   ++id;
}
id = 42;
f();  // PS: 42
f();  // PS: 43
f();  // PS: 44

// 使用3
int id = 0;
auto f = [id]() {  // [error] increment of read-only variable 'id'
   cout << id << endl;
   ++id;
}
id = 42;
f();  //
f();  //
f();  //

2、variadic templates

templates分为

  • function templates

  • class templates

变化是指两种

  • 参数个数:variadic num、

  • 参数类型

case1

void printX() {

}

template<typename T, typename...Types>
void printX(const T& firstArg, const Types&... arg) {
   cout << firstArg <<endl;
   printX(args...);
}
// 使用
printX(7.5, "hello");

case2

// 使用
cout << make_tuple(7.5, "hello") -> 预期输出[7.5, hello]
// 实现
template <typename... Args>
ostream& operator<<(ostream& os, const tuple<Args...>& t) {
   os <<"[";
   print_tuple<0, sizeof...(Args), Args...>::print(os, t);
   os <<"]";
}
// 泛化递归
template<int IDX, int MAX, typename...Args>
struct print_tuple {
   static void print(ostream& os, const tuple<Args...>& t) {
       os << get<IDX>(t) <<(IDX+1==MAX?"":",");
       print_tuple<IDX+1, MAX, Args...>::print(os, t);
  }
}
// 偏特化
template<int MAX, typename...Args>
struct print_tuple<MAX, MAX, Args...> {
   static void print(ostream& os, const tuple<Args...>& t) {}
}

case3 - 递归继承(tuple)

case4-递归符合

template<typename Head, typename...Tail>
class tuple<Head, Tail...> {
   typedef tuple<Tail...> composited;
   //
   composited m_tail;
   Head m_head;
   tuple() {}
   tuple(Head v, Tail... vtail) : m_tail(vtail), m_head(v) {}
   //
  Head head() {return m_head;}
   composited& tail() {return m_tail;}
}

 

3、右值引用

lvalue:可以出现在operator=左侧

rvalue:只能出现在operator=右侧,临时对象,表达式计算值,函数返回值

lvalue reference: 左值引用,&

rvalue referenc:右值引用,只能绑定右值,&&

const reference:常引用,可以绑定右值,const &

3.1、完美转发

一个接受右值的函数、再调用其他函数的时候,传递的右值会变为左值,这是不完美的转发

void process(int &&);
void process(int &);
void forward(int && x) {
   process(x);
}
// 使用
forward(2);  // process调用的是左值版本,这就是不完美的参数转发

std::forward 完美转发函数

3.2、移动构造

  • 自己实现类,若包含移动语义,存在指针,移动之后,来源端的把指针设置为null

    MyString opertaor=(MyString&& str) noexcept {
       if (this != &str) {
           if (_data) delete _data;
           _len = str._len;
           _data = str._data;
           str._len = 0;
           str._data = nullptr;  // 非常重要,不然str析构的时候出问题
      }
    }
  • 自己实现类,若包含移动语义,存在指针,析构函数必须进行判断

    ~MyString() {
       if (_data) delete _data;   // 和第一点配合,完成移动语义
    }
  •  
posted @ 2022-05-15 21:57  cosinehzq  阅读(93)  评论(0)    收藏  举报