C++篇:006.C++17常用新特性
一、折叠表达式
C++17中引入了折叠表达式,主要是为了方便模板编程,分为左右折叠
语法
- (形参包 运算符 ...)
- (... 运算符 形参包)
- (形参包 运算符 ... 运算符 初值)
- (初值 运算符 ... 运算符 形参包)
template<typename... Args>
bool all(Args... args){return (... && args);}
bool b = all(true, true, true, false);
//在all()中,一元左折叠展开成
//return ((true && true) && true) && false;
//b是false
二、类模板参数推导
类模板实例化时,可以不必显示指定类型,前提是保证类型可以推导
#include <iostream>
using namespace std;
template<class T>
class ClassTest
{
public:
ClassTest(T, T){};
};
int main() {
auto y = new ClassTest{100, 200};//分配的类型是ClassTest<int>
return 0;
}
auto占位的非类型模板形参
template <auto T> void func1() {
cout << T << endl;
}
func1<100> ();//OK
func1<int> ();//NO!!!
三、编译期constexpr if语句
#include <iostream>
using namespace std;
template <bool ok> constexpr void func2(){
//在编译期进行判断,if和else语句不生成代码
if constexpr (ok == true) {
//当ok为true时,下面的else快不生成汇编代码
cout << "ok" << endl;
}
else {
//当ok为false时,上面的if块不生成汇编代码
cout << "not ok" << endl;
}
}
四、inline变量
扩展的inline用法,使得可以在头文件或者类内初始化静态成员变量
//mycode.h
inline int value = 100;
//mycode.cpp
class AAA {
inline static int value2 = 200;
};
五、结构化绑定
auto stu1 = make_tuple(string{"zhangsan"}, 19, string{"man"});//tuple即元组
//没有使用结构化绑定
string name;
size_t age;
string gender;
tie(name, age, gender) = stu1;
//使用结构化绑定
auto [name, age, gender] = student;
cout << name << " " << age << " " << gender;
六、if switch 初始化
unordered_map<string, int> stu1{{"zhangsan", 18}, {"wangwu", 19}};
//C++11
auto iter = stu1.find("wangwu");
if (iter != stu1.end()) {
cout << iter->second << endl;
}
//C++17
if(auto iter = stu1.find("wangwu"); iter != stu1.end()) {
cout << iter->second << endl;
}
七、简化的嵌套命名空间
//C++17之前
namespace A{
namespace B{
namespace C{
void func1(){}
}//namespace C
}//namespace B
}//namespace A
//C++17
namespace A::B::C{
void fucn1(){}
}//namespace A::B::C
八、lambda表达式捕获*this
一般情况下,lambda表达式访问类成员变量时需要捕获this指针,这个指针指向原对象,相当于一个引用,在多线程情况下,可能lambda生命周期超过了对象,此时,对对象的访问是未定义的。
因此C++17中新增了捕获*this,此时捕获的是对象的副本。
九、charconv头文件
charconv
提供了一组高性能的字符序列与基本数值类型之间的转换函数,主要用于字符串和整数、浮点数的相互转换。
十、std::variant
有点类似于加强版的union,里面可以存放复合数据类型,且操作元素更为方便。可以用于表示多种类型的混合体,但同一时间只能用于代表一种类型的实例。
variant提供了index成员函数,该函数返回一个索引,该索引用于表示variant定义对象时模板参数的索引(起始索引为0),同时提供了一个函数holds_alternative<T>(v)
用于查询对象v当前存储的值类型是否是T
#include <iostream>
#include <variant>
#include <string>
using namespace std;
int main() {
variant<int, double, string> d;//index:int 0, double 1, string 2
cout << d.index() << endl;//输出:0
d = 3.14;
cout << d.index() << endl;//输出:1
d = "hi";
cout << d.index() << endl;//输出:2
cout << holds_alternative<int>(d) << endl;//输出:0(false)
cout << holds_alternative<double>(d) << endl;//输出:0(false)
cout << holds_alternative<string>(d) << endl;//输出:1(true),因为此时d = "hi"
}
十一、std::optional
std::optional<T>
可以存储一个类型为 T
的值,或者处于 “无值” 状态(类似 “可选值”)。例如:
-
查找操作可能找到元素(有值)或找不到(无值)。
-
函数可能返回有效结果(有值)或因参数无效而无结果(无值)。
构造方式
#include <iostream>
#include <optional>
using namespace std;
int main() {
optional<int> o1;//什么都不写默认初始化为nullptr
optional<int> o2 = nullptr;//初始化为无值
optional<int> o3 = 10;//用一个T类型的值来初始化
optional<int> o4 = o3;//用另一个optional来初始化
}
查看一个optional对象是否有值,可以直接用if
,或者用has_value()
optional<int> o1;
if (o1) {
printf("o1 has value\n");
}
if (o1.has_value()) {
printf("o1 has value\n");
}
当一个optional有值时,可以通过用指针的方式(*号和->号)来使用它,或者用.value()拿到它的值:
optional<int> o1 = 100;
cout << *o1 << endl;
optional<string> o2 = "orange";
cout << o2->c_str() << endl;
cout << o2.value().c_str() << endl;
将一个有值的optional变为无值,用.reset()
十二、std::any
C++11中引入了auto,但是auto变量一旦声明,该变量类型不可再更改
C++17中引入了std::any类型,该类型变量可以存储任何类型的值,也可以时刻改变它的类型,类似于python中的变量
#include <any>
#include <iostream>
using namespace std;
int main() {
cout << boolalpha;//输出设置:将bool值用"true"和"false"显示
any a;//定义一个空的any,即一个空的容器
//有两种方法来判断一个any是否是空的
cout << a.has_value() << endl;
cout << a.type().name() << endl;
//几种创建any的方式
ant b = 1;
auto c = make_any<float>(5.0f);
any d(6.0);
//更改any的值
a = 2;//直接重新赋值
auto e = c.emplace<float>(4.0f);//调用emplace函数,e为新生成的对象引用
//清空any的值
b.reset();
cout << b.has_value() << endl;//false
cout << b.type().name() << endl;//int
//使用any的值
try
{
auto f = any_cast<int>(a);//f为int类型,其值为2
cout << f << endl;//2
}
catch(const bad_any_cast& e)
{
cout << e.what() << endl;
}
try
{
auto g = any_cast<float>(a);//抛出std::bad_any_cast异常
cout << g << endl;//该语句不会执行
}
catch(const bad_any_cast& e)
{
cout << e.what() << endl;//可能输出Bad_any_cast
}
return 0;
}
十三、std::apply
将tuple元组解包,并作为函数的传入参数
#include <any>
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
auto add_lambda = [](auto a, auto b, auto c) {return a + b + c;};
cout << apply(add, pair(2, 3)) << endl;//5
cout << apply(add_lambda, tuple(2, 3, 4)) << endl;//9
return 0;
}
十四、make_from_tuple
解包tuple作为构造函数参数构造对象
#include <iostream>
using namespace std;
class ClassTest {
public:
string_name;
size_t_age;
ClassTest(string name, size_t age): _name(name), _age(age) {
cout << "name: " << _name << ", age: " << _age << endl;
}
};
int main() {
auto param = make_tuple("zhangsan", 19);
make_from_tuple<ClassTest>(move(param));
return 0;
}
十五、std::string_view
在C++17中,增加了std::string_view类型,它通过char*字符串构造,但是并不会去申请重新创建一份该字符串对象,只是char*字符串的一个视图,优化了不必要的内存操作。相应的,对原字符串只有读权限,没有写权限。
#include <iostream>
using namespace std;
void func1(string_view str_v) {
cout << str_v << endl;
return;
}
int main() {
const char* charStr = "hello world";
string str{ charStr };
string_view str_v(charStr, strlen(charStr));
cout << "str: " << str << endl;
cout << "str_v: " << str_v << endl;
func1(str_v);
return 0;
}
十六、std::as_const
将左值转化为const类型
##include <iostream>
using namespace std;
int main() {
string str = {"C++ as const test"};
cout << is_const<decltype(str)>::value << endl;
const string str_const = as_const(str);
cout << is_const<decltype(str_const)>::value << endl;
return 0;
}
十七、std::filesystem
方便处理文件,提供接口文件也很多,用起来很方便
头文件及命名空间
#include <filesystem>
using namespace std::filesystem
常用类
path
类:路径处理
directory_entry
类:文件入口
directory_iterator
类:获取文件系统目录中文件的迭代器容器
file_status
类:用于获取和修改文件(或目录)的属性
#include <ctime>
#include <iostream>
#include <filesystem>
using namespace std;
int main() {
namespace fs = std::filesystem;
auto testdir = fs::path("./testdir");
if(!fs::exists(testdir)) {
cout << "file or directory is not exists" << endl;
}
fs::directory_options opt(fs::directory_options::none);
fs::directory_entry dir(testdir);
//遍历当前目录
cout << "show :\t" << dir.path().filename() << endl;
for(fs::directory_entry const& entry: fs::directory_iterator(testdir, opt)) {
if(entry.is_regular_file()) {
cout << entry.path().filename() << " \t size:" << entry.file_size() << endl;
}
else if(entry.is_directory()) {
cout << entry.path().filename() << "\t dir" << endl;
}
}
cout << endl;
//递归遍历所有文件
cout << "show all:\t" << dir.path().filename() << endl;
for(fs::directory_entry const& entry : fs::recursive_directory_iterator(testdir, opt)) {
if(entry.is_regular_file()) {
cout << entry.path().filename() << "\t size:" << entry.file_size() <<"\t parent:"
<< entry.path().parent_path() << endl;
}
else if(entry.is_directory()) {
cout << entry.path().filename() << "\t dir" << endl;
}
}
}