C++篇:006

C++篇:006.C++17常用新特性

一、折叠表达式

C++17中引入了折叠表达式,主要是为了方便模板编程,分为左右折叠

语法

  1. (形参包 运算符 ...)
  2. (... 运算符 形参包)
  3. (形参包 运算符 ... 运算符 初值)
  4. (初值 运算符 ... 运算符 形参包)
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 的值,或者处于 “无值” 状态(类似 “可选值”)。例如:

  1. 查找操作可能找到元素(有值)或找不到(无值)。

  2. 函数可能返回有效结果(有值)或因参数无效而无结果(无值)。

构造方式

#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;
        }
    }
}
posted on 2025-10-15 22:28  Khalilll  阅读(5)  评论(0)    收藏  举报