当前位置: 首页 > news >正文

C++17(新特性)

目录

一 结构化绑定

二 inline 变量

三 if/switch 初始化语句

四 强制省略拷贝

五 if constexpr

六 折叠表达式

七 类模板参数自动推导

八 非类型模板参数自动推导 

九 嵌套命名空间定义

十 __has_include

十一 optional

十二 variant

十三 any

十四 string_view


一 结构化绑定

  • 结构化绑定是 C++ 的一个语法糖,允许从一个 数组,结构体,类,或者有多个数据成员对象中声明定义多个变量
auto [val1,val2,val3,....] = expression

1. 参数必须和表达式中的值一样,不能少或者多。

2. 参数的声明周期随表达式的声明周期绑定。

3. 参数是表达式的拷贝或者引用。

4. 参数不能是类的私有对象,除非在类内部定义。

5. 参数不支持嵌套定义。

示例:

int arr[3] = { 1,2,3 };
auto& [x, y, z] = arr;  // 拷贝捕捉
// auto [x, y, z,p] = arr; 必须和表达式参数一样
// auto [x, y] = arr;      必须和表达式参数一样// auto 会丢掉 顶层 const 和 引用
// 如果引用的是 const 引用,则会保留 const,否则权限放大
auto& [x, y, z] = arr;  // 左值捕捉
auto&& [x, y, z] = arr; // 万能捕捉
const auto& [x, y, z] = arr; // 左值/右值捕捉// 用于迭代器
unordered_map<string, int> Map;
for (auto& [first, second] : Map) {}class A 
{
private:int a; 
public:int b;void fun(){// 类里可以绑定私有成员auto [x, y] = *this;}
};
A a1;
// auto [x, y] = a1; 不能绑定私有成员// auto [[x,y], z] = make_pair(A(), 1); 不能嵌套定义
auto [x,y] = make_pair(A(), 1);

二 inline 变量

在 .h 文件声明对象,在多个 cpp 重复包含会引起重定义,一般的做法是 static/const/extern 来改变对象的链接属性,即对象同名,地址不同。

示例:

Tree.h
void fun(){}
int a;Tree.cc
#include "Tree.h"
// fun() 重复定义
// a 重复定义Test.cc
#include "Tree.h"---------------------------------------------Tree.h
void fun();
extern int a;Tree.cc
#include "Tree.h"
void fun(){} // 声明定义分离,定义实现
int a = 10;Test.cc
#include "Tree.h"
// 只有 fun 的声明没实现
// 只有 a 的声明---------------------------------------------Tree.h
static int a;
const int b;Tree.cc
#include "Tree.h"
a = 10,b = 10;Test.cc
#include "Tree.h"
a = 100,b = 100;两个 cc 文件的 a,b 是同名变量,地址不同

C++17 引入了 inline 变量,被 inline 修饰的变量在全局只有一份,不会像 const/static 生成同名的副本。

示例:

Tree.h
static/const int a = 10;
void fun();Tree.cc
void fun(){ cout << &a << endl; }main.cc
cout << &a << endl;
fun();00A08B94
00A08B98-------------------------------------Tree.h
inline int a = 10;
void fun();Tree.cc
void fun(){ cout << &a << endl; }main.cc
cout << &a << endl;
fun();0095B000
0095B000

修饰类静态成员对象:

普通的静态成员对象必须在类外初始化,但如果是整型家族或是枚举常量这些,用 const 可在声明的时候定义,用 constexpr 可以定义任意类型的常量。

C++17 引入 inline 修饰类静态变量,可以直接就地初始化,针对任意类型

struct A
{static const int a = 10;static const char c = 'c';// const static double a = 10; 只能修饰整型/枚举等inline static string  str = "123"; // 任意初始化即可constexpr static double s = 1.1;   // 必须用常量表达式初始化
};

三 if/switch 初始化语句

if 和 switch 的表达式可以像 for 循环一样 定义对象,判断结果。

示例:

string s = "hello world";
if (auto pos = s.find('h');pos != string::npos) { cout << s[pos] << endl; }
else cout << "no what" << endl;switch (auto pos = s.find('h');pos)
{case 0: {cout << s[0] << endl;break;}case 1: {cout << s[1] << endl;break;}default: {cout << "no what" << endl;}
}

四 强制省略拷贝

在针对函数内传值返回,返回匿名对象/有名对象,都会生成临时对象放到寄存器里,然后用寄存器里的值初始化外部的对象,因为是传值返回,这 2 个行为都涉及拷贝构造(如果没有写移动构造),带来的开销比较大,如果 move 对象之后,一次移动 + 一次移动,也有开销,C++17规定,匿名对象直接优化成直接构造中间没有拷贝/移动的行为,有名对象的返回可能涉及移动构造。

示例:

struct A
{A() { cout << "A()" << endl; }A(const A&) { cout << "A(const A&)" << endl; }A(A&&) { cout << "A&&" << endl; }
};A fun()
{A a1;return a1;
}A val = fun();// A()--------------------------------A fun()
{return A();
}A val = fun();// A()------------------------------A fun()
{A a1;return move(a1);
}A val = fun();// A()
// A&&

注意事项:

如果返回有名对象,编译器可能尝试调用移动构造进行优化,会检测有没有移动构造,如果强制删除,则会报错,如果是匿名对象则不会检测,删除也能跑通。

struct A
{A() { cout << "A()" << endl; }A(const A&) { cout << "A(const A&)" << endl; }A(A&&) = delete; //{ cout << "A&&" << endl; }
};A fun()
{A a1;return a1;
}
// 无法引用 函数 "A::A(A &&)" (已声明 所在行数:3684) -- 它是已删除的函数// 匿名对象不用检测移动构造的逻辑
A fun()
{return A();
}

五 if constexpr

常规的 if 判断是运行时的逻辑,且会生成所有分支的代码,参数可以是任意表达式,但会受模板限制。

if constexpr:编译时的逻辑,仅生成某个分支的代码,其他分支的代码丢掉,只能传入常量表达式,不受模板限制。

示例:

template <typename T>
void fun(T val)
{if(1) { cout << val.size() << endl; }else cout << val++ << endl;
}string s;
fun(s);// 常规的 if 会生成所有分支的代码, string 不能 ++,编译错误 void fun(T val)
{if constexpr (1) { cout << val.size() << endl; }else cout << val++ << endl;
}// if constexpr 会舍弃其他分支的代码, val++ 被舍弃调,编译通过 

另一个玩法,解决模板元编程 SFINAE 替换失败不是错误,尝试匹配其他的版本,定义多个函数模板来决定实例化对应的类型,用 if constexpr 不用写冗余函数。

示例:

template <typename T>
auto fun(T val) -> decltype(val.size())
{cout << "string" << endl;return val.size();
}template <typename T>
auto fun(T val) -> decltype(val++)
{cout << "int" << endl;return val;
}-----------------------------template <typename T>
void fun(T val)
{if constexpr (is_integral_v<T>) cout << "int" << endl;else cout << "string" << endl;
}

六 折叠表达式

C++11 提供的参数包解包的时候需要用到空函数来决定终止条件。

示例:

void fun() {}template <class T, class ...Args>
void fun(T first,Args... args)
{cout << first << endl;fun(args...);
}

C++17 引入折叠表达式就不必再写冗余函数,直接在主函数内部展开所有逻辑。

必须使用二元运算符:+ * / % ^ & |= < > << >> += = *= /= %= ^= &= |= <<= >>= == != <= >= && || , . * ->*

左折叠:从左往右运算。

右折叠:从右往左运算。

1. 一元折叠表达式

一元左折叠

template <class ...Args>
int fun(Args... args)
{return (... + args);
}

一元右折叠m

template <class ...Args>
int fun(Args... args)
{return (args + ...);
}

2. 二元折叠的表达式

二元左折叠

template <class ...Args>
int fun(Args... args)
{return (val + ... + args);
}

二元右折叠

template <class ...Args>
int fun(Args... args)
{return (args + ... + val);
}

函数嵌套:

template <class T>
T _fun(T val)
{return val;
}template <class ...Args>
void fun(Args... args)
{cout << ('p' + ... + (_fun(args))) << endl;
}fun(string("1"), string("2"), string("3"), string("4"));// 二元左折叠

注意事项:逗号表达式无论什么情况都是从左往右执行,不管是左折叠还是右折叠。

七 类模板参数自动推导

函数模板可以显示实例化也可以隐式编译器自动推导类型,类模板必须显示实例化,C++17 引入类模板参数自动推导,就不需要显示的写类型了。

示例:

tuple<int, string, double, char> tp1 = { 1,"1",1.1,'1' };
tuple tp2 = { 1,"1",1.1,'1' };vector v = { 1 };

八 非类型模板参数自动推导 

在模板中,类型模板用 class/typename 修饰,非类型模板指定类型修饰,但写死了,C++17 引入 auto 可以自动推导非类型模板参数的类型,但不是所有类型支持。

示例:

template </* size_t N */ auto N>
class A {};
const char s[] = "123";A<1> a1;
A<1.1> a2; // C++ 20 支持
A<'a'> a3;
A<false> a4;
A<s> a5;

做参数包:

template <auto... N>
class A {};A<1, 'c', 1.1> a6;

九 嵌套命名空间定义

C++为了区分同名变量通过域作用限定符来访问不同的对象,也就是对象被封到命名空间李了,如果命名空间嵌套的太深了,太冗余了,C++ 17 给了一套新的定义方式。

示例:

// C++ 17 之前
namespace A
{namespace B{namespace C{}}
};// C++ 17
namespace A::B::C
{ int a = 10; 
}namespace xxx= A::B::C;A::B::C::a = 20;
xxx::a = 100;

十 __has_include

__has_include(<头文件>):预处理阶段判断某个头文件在不在。

示例:

#if __has_include(<vector>)string s = "vector";
#elsestring s = "unknow";
#endif cout << s << endl;// vector

十一 optional

在调用某个接口,查找某个对象的时候找不到,string返回 string::npos,链表返回 nullptr,或者一些其他的处理结果,这些返回结果都不统一,可能返回的值就是想要的值,C++17 引入了 optional 来保存返回的结果是合法还是不合法的。

常见的接口:

value:     拿取里面的值,不存在结果未定义
value_or:  拿取里面的值,不存在返回 nullopt_t
has_value/operator bool: 判断是否有值
reset:     释放里面的值

示例:

optional<char> fun()
{string s = "hello world";if (auto it = s.find('h');it != string::npos){return optional<char>(s[it]);}else return optional<char>();
}optional<char> op = fun();if (op.has_value()/* op */)
{cout << op.value() << endl;
}
else cout << "nullopt" << endl;

十二 variant

C语言中的 union 联合体,所有成员对象共享最大的那个对象的空间,使用时容易难以避免错误,比如把存储的是 int ,访问 char 对象,行为的未定义的,C++17 引入了 variant<T...>,对联合体更细致的封装。

variant<T...>:能容纳任意多个类型,提供全局的多个访问接口,不合法则抛异常或其他行为。

常用接口:

index()             : 返回实际存的值的下标
emplace<下标/类型>  : 插入数据,指定下标/类型插入
get<下标/类型>      : 根据下标/类型获取值,没有抛异常
get_if<下标/类型>   : 根据下标/类型获取值,没有返回空
visit<可调用对象/vairant<T...>对象>: 根据传入的 vairant 对象编译时确定实际的存储的值,传给可调用对象

示例:

variant<int, string> v = 1;
cout << "int -> " << v.index() << endl;
v = "hello world";
cout << "string -> " << v.index() << endl << endl;v.emplace<int>(10);
v.emplace<0>(10);v = 1;
int a1 = get<int>(v);
// string str1 = get<string>(v); // str1 行为未定义int* a2 = get_if<int>(&v);
string* str2 = get_if<string>(&v); // str2 为空
cout << "a2 ->" << a2 << " " << "*a2 ->" << *a2 << endl;
cout << "str2 ->" << str2 << endl << endl;visit([](auto&& x){// 得到 x 的原始类型,decltype 会保留顶层 const 和 引用using type = decay_t<decltype(x)>;if constexpr (is_same_v<int, type>){cout << "int ->" << x << endl;}else if constexpr (is_same_v<int, type>) cout << "int ->" << x << endl;else cout << "unknow" << endl;}, v);------------------------------------------int -> 0
string -> 1a2 ->004FF738 *a2 ->1
str2 ->00000000int ->1

十三 any

上面说的 variant 针对知道想要什么类型,在这些类型之间自由切换,而 any 则是针对不知道要什么类型,可以接收任意类型而存在的。

常用接口:

emplace<T> : 显示插入指定类型对象
reset      : 释放存储的对象
has_value  : 判断是否有对象
type       : 返回对象的类型
any_cast   : 得到里面的对象

示例:

any a1 = 1;
a1.emplace<string>("123");
a1.emplace<int>(10);a1.reset();
if (a1.has_value())
{//如果类型不匹配则抛异常cout << any_cast<int>(a1) << endl;
}
else cout << "unknow" << endl;a1 = 1;
const type_info& ti = a1.type();
cout << ti.name() << endl;----------------------------
unknow
int

variant VS any

  • variant 针对知道要什么类型,any 不知道要什么类型
  • variant 的对象在自己内部,any 的小对象在自己内部,大对象用指针指向堆
  • variant 提供 visit 编译时检测对应的类型值,any 要写 if else any_cast 运行时判断

十四 string_view

传统的 std::string ,可能涉及拷贝构造,移动构造等操作,会影响效率,C++ 17 引入了 string_view 来作为 string 的视图,也就是观察他,不修改它,和 weak_ptr 类型。

他的接口和 string 类型,没有修改接口。

string_view默认不包含 '\0'。

示例:

// 无参构造,底层指针为空,string 会多开辟一个空间存储 '\0'
string_view sv1;// 常量字符串构造
string_view sv2("123");// string 构造
string s1 = "hello world";
string_view sv3(s1);cout << "sv3 -> " << sv3 << endl;
cout << "sv3 -> ";
for (auto& e : sv3)
{// e = 'a'; 不可修改cout << e;
}
cout << endl;// 从左往右调整指针指向
sv3.remove_prefix(6);// 从右往左调整指针指向
// sv3.remove_suffix(6);
cout << "sv3 -> " << sv3 << endl;// 取里面的指针
const char* p1 = sv3.data();
cout << "p1 -> " << *p1 << endl;// 截取子串,没有深拷贝
string_view sv4 = sv3.substr(0,5);if (auto it = sv4.find('w');it != string_view::npos)
{cout << "sv4[it] -> " << sv4[it] << endl;
}---------------------------sv3 -> hello world
sv3 -> hello world
sv3 -> world
p1 -> w
sv4[it] -> w

注意事项:

  • string_view 指向的是局部对象,局部对象销毁,string_view 则是未定义的
  • string_view 不存储 '\0',使用一些 C 风格的API行为可能未定义
http://www.dtcms.com/a/553427.html

相关文章:

  • 韩国风网站什么网站有做册子版
  • day58-Shell编程(第四部分)
  • 用AI写了一个文档拼音标注工具 中文+拼音一键生成
  • 做网站还有意义同样是div 怎么有些网站收录少 有些多
  • 企必搜做网站做国际物流在哪些网站找客户
  • 移动端适配完全指南:从基础到最佳实践
  • 使用JMeter进行API性能压测(执行篇)
  • IntelliJ IDEA 远程调试(Remote Debugging)教程
  • 网站服务器++免费做电子手抄报的网站
  • 单页网站的优点网络公司是做什么的?
  • 阿瓦隆 Q 90T矿机:低功耗高效挖矿,是否值得选择?
  • 印度实时股票数据源接口对接文档-IPO新股、k线数据
  • HTTPS接口国密安全设计(含防重放设计)
  • 网站设计公司(信科网络)中国制造网外贸平台怎么注册
  • 网站模版如何去除title版权信息499元做网站
  • 武进建设局网站首页胖鼠wordpress
  • 机器学习第一阶段
  • Linux内核RDMA用户态内存映射机制深度解析:零拷贝高性能的基石
  • 组态软件和实时数据库区别大吗?
  • SpringBoot】Spring Boot 项目的打包配置
  • 递归专题5 - FloodFill算法专题
  • 系统架构设计师论文-论软件架构的复用
  • 沙市做网站weiswordwordpress微信登录设置
  • 理解MySQL的原理
  • Mac通过命令行开启ssh服务
  • 哈尔滨有哪些做网站的公司站长工具seo综合查询问题
  • 珠海做网站的wordpress 写作
  • 【计算机基础】之核心架构
  • 临西网站建设公司公司核名查询官网
  • PPIO独家上新GPU实例模板,一键部署Kimi-Linear