C++新版本特性

目录:

前言

C++11的常用新特性

auto类型推导:

 auto的限制:

auto的应用: 

decltype类型推导:

decltype的实际应用: 

使用using 定义别名: 

 支持函数模板的默认模板参数 :

 tuple元组:

列表初始化:

 lambda匿名函数:

lambda的实际应用 :

for循环:

constexpr: 

右值引用:

std::move

std::forward

nullptr

 std::bind

 std::function

C++14的常用新特性

模板变量: 

 别名模板:

泛型lambda和lambda初始化捕捉

放松对constexpr函数的限制

 deprecated标记:

二进制字面量和数位分隔符: 

 折叠表达式:

类模板参数推导: 

 auto占位的非类型模板形参:

 编译期constexpr if语句/constexpr的lambda表达式:

内联变量:

结构化绑定:

if初始化:

using声明语句可以声明多个名称:


前言

本篇博客分享,对于c++11,14,17的常用c++新版本特性~


C++11的常用新特性

auto类型推导:

C++赋予auto关键字新的含义,用它做于自动类型推导

使用了auto关键字后,编译器会在编译阶段自动推导出变量的类型 

基本使用语法:

auto name = value;

name是变量名字,value是变量的初始值

注意:auto只是一个占位符,在编译期间会被真正的类型所替代,并不违背C++中的变量名必须有明确类型这一规定,只是这个是编译器自动给你推导的 

 auto的限制:

  • auto 不能在函数的参数中使用(auto 要求必须对变量进行初始化)
  • auto 不能作用于类的非静态成员变量(也就是没有 static 关键字修饰的成员变量)中
  • auto 关键字不能定义数组
  • auto 不能作用于模板参数

auto的应用: 

范围for:

int main()
{std::vector<int> vi{ 3,5,1,6,7,2,8 };for (auto e : vi) {std::cout << e << " ";}return 0;
}//输出结果:3 5 1 6 7 2 8

以及泛型编程中~ 

decltype类型推导:

 auto 和 decltype 关键字都可以自动推导出变量的类型,但它们的用法是有区别的:

auto name = value;decltype(exp) name = value;

其中name便是变量名,value表示赋给变量的初值~~

auto 根据 = 右边的初始值value推导出变量的类型

decltype根据圆括号内的exp表达式推出变量类型,和 = 右边的value无关

TIP:auto变量一定要初始化,decltype不用,很好理解,auto推导他需要初始化才推得动 

decltype的实际应用: 

下面就是使用的decltype推导的类型,避免使用T::iterator获得容器迭代器指针,因为有些自己实现的容器并没有iterator 

使用using 定义别名: 

C++11中的using 写法跟typedef 等价

template<typename V>
using smap = std::map<std::string, V>;void test_using()
{smap<int> mp1;mp1["score"] = 100;mp1["age"] = 18;smap<string> mp2;mp2["name"] = "Merge";mp2["address"] = "CN";std::cout << mp1["age"] << ": " << mp1["score"] << endl;std::cout << mp2["name"] << ": " << mp2["address"] << endl;}// 输出:
// 18: 100
// Merge: CN

 支持函数模板的默认模板参数 :

 C++11 支持为函数模板中的参数设置默认值,在实际使用过程中,我们可以选择使用默认值,也可以尝试由编译器自行推导得到,还可以亲自指定各个模板参数的类型

template <typename R = int, typename T, typename U>
R func(T v1,U v2)
{return v1+v2;
}void testFunTemplate()
{auto r1=func(5.5,10);                    auto r2=func<double>(5.5,10);            auto r3=func<double, int, int>(5.5,10); cout<<"r1:"<<r1<<endl;cout<<"r2:"<<r2<<endl;cout<<"r3:"<<r3<<endl;
}

 tuple元组:

tuple的最大特点是:实例化的对象可以存储任意数量、任意类型的数据

例如要储存多个不同类型的元素时;需要函数返回多个数据的时,可以存储在tuple中后返回

void tuple_test()
{std::tuple<int, char> fir;std::tuple<int, char> mid(std::make_pair(2, '2'));      //{2,'2'}std::tuple<int, char> sec(1, '1');                      //右值初始化std::tuple<int, int, char> thd(make_tuple(6, 6, '6'));  //右值初始化std::tuple<int, char> fourth(sec);                      //左值初始化std::cout << std::get<0>(fourth) << " : " << std::get<1>(fourth) << std::endl;std::cout << std::get<0>(thd) << " : " << std::get<1>(thd) << " : " << std::get<2>(thd) << std::endl;
}
//输出:
//1 : 1
//6 : 6 : 6

列表初始化:

 在 C++11 中,初始化列表的适用性被大大增加了。它现在可以用于任何类型对象的初始化


class Base
{
public:Base(int){}};
int main()
{Base b1(123);Base b2 = 123;Base b3 = { 123 };Base b4{ 123 };//c++11加入return 0;
}//上述这些都是可以给定义的对象赋初值的

 lambda匿名函数:

C++11中引入了lambda~~

定义一个lambda匿名函数:

[](){}

这就是lambda的表达式三个括号,省略返回值

[外部访问方式说明符](参数){函数体};
外部变量格式                                          功能
[]        空方括号表示当前 lambda 匿名函数中不导入任何外部变量
[=]只有一个 = 等号,表示以值传递的方式导入所有外部变量
[&]只有一个 & 符号,表示以引用传递的方式导入所有外部变量
[val1,val2,...]表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序
[&val1,&val2,...]表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序
[val,&val2,...]以上 2 种方式还可以混合使用,变量之间没有前后次序
[=,&val1,...]表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入
[this]表示以值传递的方式导入当前的 this 指针

lambda的实际应用 :

 使用Lambda表达式在向量中查找大于10的元素并打印出来:


int main()
{vector<int> vi{ 1,4,21,12,13,9,30,24 };std::for_each(vi.begin(), vi.end(), [](int num){if (num > 10) std::cout << num << " ";});return 0;
}
//输出:21 12 13 30 24

 使用Lambda表达式实现自定义排序规则,并对一个字符串数组进行排序:


int main()
{vector<string> vs{ "Alice","Bob","Merial","Lucy","GorzenHot" };sort(vs.begin(), vs.end(), [](const string s1, const string s2) {return s1.size() < s2.size();});for (auto e : vs)cout << e << " ";return 0;
}
//输出:Bob Lucy Alice Merial GorzenHot

for循环:

 C++ 11 标准为 for 循环添加了一种全新的语法格式

for (declaration : expression){//循环体
}

示例即对于上面lambda的遍历方式~~

constexpr: 

constexpr关键字能够让指定的常量表达式在程序编译阶段计算出结果,而不需要到运行阶段
c++11中constexpr可用于修饰普通变量、函数、类的构造函数
int main()
{constexpr int num = 1 + 2 + 3;int arr[num]{ 1,2,3,4,5,6 };cout << arr[5];return 0;
}
//输出:5

右值引用:

C++11标准引入的另一种应用方式,称为右值引用。从而引出了移动语义和完美转发~~

声明和左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化。

int num = 1;int && a = 10;a = 20;
cout<<a<<endl;
//输出:20

右值也必须使用右值进行初始化

std::move

C++11最重要的一个改进之一就是引入了move语义,这样在一些对象的构造时可以获取到已有的资源(如内存)而不需要通过拷贝,申请新的内存,这样移动而非拷贝将会大幅度提升性能

class Base {
public:Base():num(new int(0)){cout << "构造函数" << endl;}Base(const Base& b) :num(new int(*b.num)) {cout << "拷贝构造函数" << endl;}Base(Base&& b) :num(b.num) {b.num = nullptr;cout << "移动构造函数" << endl;}~Base() {delete num;cout << "析构函数" << endl;}
public:int* num;
};int main()
{int* num = new int(20);Base a;Base b(a);Base c(std::move(b));return 0;
}//构造函数
//拷贝构造函数
//移动构造函数//析构函数
//析构函数
//析构函数

在移动构造的时候,没有新的内存申请和分配,有的只是资源转移,在大量对象的系统中,移动构造相对拷贝构造可以显著提升性能!

std::forward

完美转发:函数模板可以将自己的参数“完美”的转发给内部调用接口,不仅仅能够转移参数的值,还能转移参数的左右属性不变~~

void FTest(int& t) {cout << "左值" << endl;
}
void FTest(int&& t) {cout << "右值" << endl;
}
template <typename T>
void func(T&& t) {FTest(std::forward<T>(t));
}void Test_Forward()
{string A = "abc";string&& Rval = std::move(A);string B(Rval);       //拷贝构造,不是移动构造cout << A << endl;    //此时A的资源已经被转移到 Rval了string C(std::forward<string>(Rval));//移动构造,保留了Rval的属性为右值,否则如果不在这 //里std::move()的话,并不能完美转发属性func(5);int x = 1;func(x);
}//输出结果: abc
//          右值
//          左值

nullptr

C++11下,相比于NULL和0,使用nullptr初始化空指针,可以使我们编写的程序更健壮


void isnull(void* c) {cout << "void*c" << endl;
}
void isnull(int n) {cout << "int n" << endl;
}void testNullptr()
{cout << "testNullptr:" << endl;isnull(0);//isnull(NULL); // C++ 98/03 标准中 输出 int nisnull(nullptr);
}int main() {testNullptr();return 0;
}
//输出:testNullptr:
//     int n
//     void*c

 std::bind

std::bind

函数原型:

template<class Fn, class... Args>
std::bind(Fn&& fn, Args&&... args);

对其参数的解释:

  • Fn:是要绑定的可调用对象(函数,函数指针,成员函数,函数对象等)
  • Args:是对应Fn的参数列表 

std:bind函数返回一个新的可调用对象,可以同股调用该对象执行绑定的函数,并传递额外的参数.

示例:

void foo(int a, int b)
{cout << "a: " << a << "\t" << "b: " << b << endl;std::cout << "Sum : " << a + b << endl;
}
int main() {auto SumFunc = std::bind(foo,placeholders::_1,10);SumFunc(10);return 0;
}//输出: a: 10   b: 10
//      Sum : 20

每个参数都可以绑定一个值或是占位符上:

  • 如果绑定一个值,那么调用返回的函数对象就会使用该值作为参数
  • 如果是一个占位符,则调用返回的函数对象会转发一个传递给调用的参数(参数顺序由占位符决定)

 std::bind的注意事项:

  • bind预先绑定的参数需要传递具体的变量或者值进去,对于预先绑定的参数是pass-by-value。除非被std::cref包装,才pass-by-reference
  • 对于事先不绑定的参数,就需要传入std::placeholders进去从 _1开始递增,这些placeholder是pass-by-reference的
  • bind的返回值是可调用的实体,可以赋值给std::function对象
  • 对于绑定的指针、引用类型参数,需要保证在调用实体之前,这些参数是可用的
  • 类的this可以通过对象或者指针来绑定

 std::function

类模板std::function是一种通用、多态的函数封装。std::function的示例可以对任何可调用的目标实体进行存储、复制、调用和操作。这些包括 普通函数、lambda表达式,bind表达式、函数指针及其他函数对象~~


C++14的常用新特性

模板变量: 

在C++14中:

  •  lambda表达式参数可以为auto类型 ,类似于函数模板
  • 可以对捕捉列表的捕获变量“赋值”
template<typename T>
constexpr T pi = T(3.1415926535897932385); //变量模板template<typename T>
T circular(T r) {return pi<T> *r * r;
}
int main()
{cout << circular(2) << endl;cout << circular(2.00) << endl;return 0;
}//输出: 12
//      12.5664

 别名模板:


template<typename T ,typename U>
struct A {T t;U u;
};template<typename T>
using B = A<T, int>;int main()
{B<double> b;b.t = 12.12;b.u = 34.12;cout << b.t << " " << b.u << endl;
}//输出:12.12 34
//这里模板U已经被特化成int 类型,并且定义新的别名模板 B
//从此B就是一个T类型 加 一个 int 类型 的A模板

泛型lambda和lambda初始化捕捉

int main()
{int a = 2;auto fa = [a = sin(a)]() {cout << a << endl;cout << cos(a) << endl;};fa();cout << a << endl;//lambda表达式中参数可以为auto了auto f = [](auto a) {return a;};cout << f(1.1) << " " << f(298) << endl;return 0;
}
//输出结果:
// 0.909297
// 0.6143
// 2
// 1.1 298

放松对constexpr函数的限制

C++11中的常量表达式函数:

  • 函数体只有单一的return返回语句
  • 函数必须有返回值(不能是void函数)
  • 在使用前必须有定义
  • return返回语句表达式中不能使用非常量表达式的函数,全局数据,必须是一个常量表达式
#include <cmath>
#include <iostream>
using namespace std;constexpr int factorial(int n) { // 在C++11或者C++14中均可以编译通过return n <= 1 ? 1 : (n * factorial(n - 1));
}constexpr int factorial_c14(int n) { // 只有在C++14中可以编译通过int ret = 1;for (int i = 1; i <= n; ++i) {ret *= i;}return ret;
}int main()
{std::cout << factorial(5) << std::endl;   // 120std::cout << factorial_c14(5) << std::endl; // 在c++11下,会报error: body of ‘constexpr’ function ‘constexpr int factorial_c14(int)’ not a return-statementreturn 0;
}

 deprecated标记:

C++14中增加了[[deprecated]]标记,可以修饰类、函数、变量等,当程序中使用了被其修饰的模块时,编译器会产生告警,提示用户该标记标记的内容将来可能会被废弃,尽量不要使用

二进制字面量和数位分隔符: 

int main()
{int a = 0b0001'1111'1010;double b = 3.14'1592'6535;cout << a << " " << b << endl;return 0;
}
//输出:506 3.14159

 折叠表达式:

template <typename... Args>
auto sub_right(Args... args)
{return (args - ...);
}template<typename... Args>
auto sub_left(Args... args)
{return (... - args);
}
template<typename... Args>
auto sum_right(Args... args)
{return (args + ...);
}int main()
{cout << sub_right(10, 6, 4) << endl; //(10 - (6 -4)) = 8cout << sub_left(10, 6, 4) << endl;  //((10 - 6) - 4)= 0cout << sum_right(10, 6, 4) << endl; //(10 + ( 6 + 4 )) = 20return 0;
}
//输出:8 0 20

类模板参数推导: 

类模板实例化时,可以不必显示指定类型,前提是保证类型可被推导

template<typename T>
class A {
public:A(T,T){}
};int main() {auto a = new A{ 1,2 };   //A<int> A<int> *b = new A(3, 4); //A<int> std::tuple t(3, 4, 5.6); //std::tuple<int,int,double> t
}

 auto占位的非类型模板形参:

template<auto T>
void foo()
{cout << T << endl;
}int main()
{foo<100>();return 0;
}
//输出: 100

 编译期constexpr if语句/constexpr的lambda表达式:

lambda表达式可以再编译期进行运算,且函数体不能包含汇编语句,goto语句,try块,静态变量,线程局部存储,没有初始化的普通变量,不能动态分配内存,不能new delete等,不能为虚函数 

#include <assert.h>template<auto flag>constexpr void foo()
{//在编译期间进行判断,if和else语句不生成代码if constexpr (flag == true) {       //当flag为true的时候,下面的else块不生成汇编代码cout << "flag == true" << endl;}else  {cout << "flag == false" << endl;}
}
int main() {foo<true>(); //输出flag == true ,并且汇编代码中只有 cout<<"flag == true"<<endl;constexpr auto foo = [](int a, int b) {return a + b;};static_assert(foo(2, 3) == 5, "compile-time assert");//静态断言,用于编译期的断言return 0;
}

 static_assert:是静态断言,在编译期的断言,而constexpr就可以在编译期得到结果,从而提前判断.

内联变量:

扩展的inline用法,使得可以在头文件或者类内初始化静态成员变量:


// test.h
inline int val = 1;// test.cpp
struct A
{inline static int val = 1;
};  

结构化绑定:

在C++11中,如果需要获取tuple元素,需要使用get<>()函数或者tie<>函数,这个函数可以把tuple中的元素转化为可以绑定到tie<>()左值的集合。

C++17中的结构化绑定,大大方便了这种操作,而且使用引用捕捉时还可以修改捕捉对象的值

#include<unordered_map>
#include<tuple>void test1() {tuple person = std::make_tuple(string{ "XiaoHei" }, 26, string{ "man" });string name;int age;string gender;tie(name, age, gender) = person;cout << name << " " << age << " " << gender << endl;
}struct Student {string name;int age;
};Student getStudent() {return { "xiaobai",22 };
}
void test2()
{tuple stu = make_tuple(string("Xiaohei"), 18, string("man"));auto [name, age, gender] = stu;//这里的auto推导出//std::tuple<string,int,string> 类型cout << name << " " << age << " " << gender << endl;//这是按tuple 的结构化绑定auto [_name, _age] = getStudent();//这个stu就被推成一个pair 类形了auto stu_cls = getStudent();cout << _name << " " << _age << endl;unordered_map<string, int> mstu;mstu.emplace("张三", 33);mstu.emplace("李四", 44);for (auto [name, age] : mstu) {cout << name << " " << age << endl;}}   int main()
{test1();test2();return 0;
}

使用结构化绑定,可以将一个复杂对象(元组,map,make_pair,包含多个成员的结构体 等)分解其各个成员,并将这些成员绑定到对应的变量当中,无需显示的通过函数或者成员访问来获取每个成员的值.

if初始化:

#include <iostream>
#include <unordered_map>
#include <tuple>void c11_fun()
{std::unordered_map<std::string, int> students{{"liBai", 18}, {"hanXin", 19}};auto iter = students.find("hanXin");if (iter != students.end()){std::cout << iter->second << std::endl;}
}void c17_fun()
{std::unordered_map<std::string, int> students{{"liBai", 18}, {"hanXin", 19}};if (auto iter = students.find("hanXin"); iter != students.end()){std::cout << iter->second << std::endl;}
}int main()
{//c11_fun();c17_fun();return 0;
}

using声明语句可以声明多个名称:

using std::cout , std::endl;

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/460004.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux操作系统基础(三):虚拟机与Linux系统安装

文章目录 虚拟机与Linux系统安装 一、系统的安装方式 二、虚拟机概念 三、虚拟机的安装 四、Linux系统安装 1、解压人工智能虚拟机 2、找到解压目录中的node1.vmx 3、启动操作系统 虚拟机与Linux系统安装 一、系统的安装方式 Linux操作系统也有两种安装方式&#xf…

从零开始手写mmo游戏从框架到爆炸(十)— 集成springboot-jpa与用户表

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 集成springboot-jpa&#xff0c;不用mybatis框架一个是方便对接不同的数据源。第二个目前规划的游戏内容可能对数据库的依赖不是很大&#xff0c;jpa应该肯定能满足要求了…

阿里云服务器价格表2024最新版CPU内存带宽报价

2024年2月阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核…

2024.2.7日总结(小程序开发4)

页面导航 页面导航是页面之间的相互跳转&#xff1a; <a>链接location.href 小程序中实现页面导航的两种方式&#xff1a; 声明式导航 在页面上声明一个<navigator>导航组件通过点击<navigator>组件实现页面跳转 编程式导航 调用小程序的导航API&…

[C#] 如何使用ScottPlot.WPF在WPF桌面程序中绘制图表

什么是ScottPlot.WPF&#xff1f; ScottPlot.WPF 是一个开源的数据可视化库&#xff0c;用于在 WPF 应用程序中创建高品质的绘图和图表。它是基于 ScottPlot 库的 WPF 版本&#xff0c;提供了简单易用的 API&#xff0c;使开发人员能够通过简单的代码创建各种类型的图表&#…

极值图论基础

目录 一&#xff0c;普通子图禁图 二&#xff0c;Turan问题 三&#xff0c;Turan定理、Turan图 1&#xff0c;Turan定理 2&#xff0c;Turan图 四&#xff0c;以完全二部图为禁图的Turan问题 1&#xff0c;最大边数的上界 2&#xff0c;最大边数的下界 五&#xff0c;…

CentOS7集群配置免密登录

准备工作 提前开启三台虚拟机hadoop102、hadoop103,hadoop104,关于三台虚拟机的安装可以参考&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/136010108 配置免密登录 一、分别修改三台机器的hosts,配置主机映射关系 vim /etc/hosts 文件中输入以下内容&#xf…

Web 目录爆破神器:Dirb 保姆级教程(附链接)

一、介绍 dirb 是一款用于目录爆破的开源工具&#xff0c;旨在帮助渗透测试人员和安全研究人员发现目标网站上的隐藏目录和文件。它使用字典文件中的单词来构建 URL 路径&#xff0c;然后发送 HTTP 请求来检查这些路径是否存在。 以下是 dirb 工具的一些特点和基本用法&#…

leetcode206反转链表|详细算法讲解学习

题目 https://leetcode.cn/problems/reverse-linked-list/ 这道题对于刚开始学习数据结构和算法的人来说有点难&#xff0c;是入门的重要典型题目&#xff1b;但等数据结构入门之后&#xff0c;这就会是一道非常简单的题目了。 算法一&#xff08;算法正确但超出时间限制&am…

SQL在云计算中的新角色:重新定义数据分析

文章目录 1. 云计算与数据分析的融合2. SQL在云计算中的新角色3. 分布式SQL查询引擎4. SQL-on-Hadoop解决方案5. SQL与其他数据分析工具的集成6. 实时数据分析与SQL7. SQL在云数据仓库中的角色8. 安全性与隐私保护9. SQL的未来展望《SQL数据分析实战&#xff08;第2版&#xff…

python创建udf函数步骤

一、目标 实现一个函数&#xff0c;传入两个datetime类型的参数&#xff0c;返回double类型的工作日天数 二、思路 如何计算差值&#xff1f; 如果开始时间和结束时间在同一天&#xff1a;实现同 datediff(end, start, ‘ss’) / 86400.0 如果开始时间和结束时间在不同天&am…

通过docker-compose部署NGINX服务,并使该服务开机自启

要在通过docker-compose部署的NGINX服务实现开机自启&#xff0c;你需要确保Docker守护进程在系统启动时自动运行&#xff0c;并配置docker-compose.yml文件以在容器中运行NGINX服务。以下是步骤&#xff1a; 确保Docker守护进程开机启动&#xff1a; 在Ubuntu/Debian上&#x…