C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
  • 技术文档
  • 前沿资讯
  • 常用软件
  • 在线工具
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
  • 技术文档
  • 前沿资讯
  • 常用软件
  • 在线工具
关于
  • 基础

    • C++ 基础 - 学习路线
    • C++ 基础 - 知识点
    • C++ 基础 - 面向对象
    • C++ 基础 - 语法糖
    • C++ 基础 - 关键字
    • C++ 基础 - 常用宏定义
    • C++ 基础 - 宏函数、内联函数、普通函数
  • 函数

    • C++ 函数 - 闭包
    • C++ 函数 - Linux系统调用Hook
    • C++ 函数 - getopt函数分析以及命令行解析
    • C++ 函数 - 函数指针及其应用
    • C++ 函数 - 作用域运算符::的使用
    • C++ 函数 - 智能指针shared_ptr的使用
    • C++ 函数 - struct结构体学习
    • C++ 函数 - typedef 语句的使用
    • C++ 函数 - va_list函数学习
  • 知识点

    • C++ 知识点 - 写时拷贝技术(copy-on-write)
    • C++ 知识点 - 前向声明(forward declaration)
    • C++ 知识点 - 头文件.h 和 源文件.cpp 以及多重定义(multiple definition)
    • C++ 知识点 - 为什么C语言不支持函数重载,C++又是如何支持的
    • C++ 知识点 - return 局部变量
    • C++ 知识点 - linux下c/cplusplus头文件和动态库的搜索
    • C++ 知识点 - 模板特化与偏特化
  • IO框架

    • C++ IO框架 - I/O 复用
    • C++ IO框架 - select 函数
    • C++ IO框架 - poll 函数
    • C++ IO框架 - epoll 函数
    • C++ IO框架 - Reactor 和 Proactor
  • 新版本特性

    • C++ 新版本特性 - C++11
    • C++ 新版本特性 - C++14
    • C++ 新版本特性 - C++17
    • C++ 新版本特性 - C++20

C++ 新版本特性 - C++17

    参考资料

    • cpp17
    • C++17新特性总结

    特性概览

    c++17_1

    c++17_2

    c++17_3

    c++17_4

    折叠表达式

    C++17中引入了折叠表达式,主要是方便模板编程,分为左右折叠,下图为其解包形式:

    1. Unary right fold (E op ...) becomes (E1 op (... op (EN-1 op EN)))
    2. Unary left fold (... op E) becomes (((E1 op E2) op ...) op EN)
    3. Binary right fold (E op ... op I) becomes (E1 op (... op (EN−1 op (EN op I))))
    4. Binary left fold (I op ... op E) becomes ((((I op E1) op E2) op ...) op EN)
    详情
    #include <iostream>
    #include <vector>
    
    template <typename... Args>
    void printer(Args &&...args)
    {
    	(std::cout << ... << args) << '\n';
    }
    
    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()
    {
    	printer(1, 2, 3, "abc");
    
    	std::cout << sub_right(8, 4, 2) << std::endl; // (8 - (4 - 2)) = 6
    	std::cout << sub_left(8, 4, 2) << std::endl;  // ((8 - 4) - 2) = 2
    
    	std::cout << sum_right(std::string{"hello "}, std::string{"world"})
    			  << std::endl; // hello world
    	
    	return 0;
    }
    
    

    运行结果:

    详情
    [root@iZuf61kbf845xt6tz10abgZ c17]# g++ -std=c++17 fold_expressions.cpp -o fold_expressions
    [root@iZuf61kbf845xt6tz10abgZ c17]# ./fold_expressions
    123abc
    6
    2
    hello world
    
    

    类模板参数推导

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

    详情
    #include <utility>
    #include <tuple>
    #include <iostream>
    #include <functional>
    #include <algorithm>
    
    template <typename T>
    class A
    {
    public:
    	A(T, T){};
    };
    
    int main()
    {
    	std::pair p(2, 4.5);	 // std::pair<int, double> p
    	std::tuple t(4, 3, 2.5); // std::tuple<int, int, double> t
    	std::less l;			 // std::less<void> l
    	auto y = new A{1, 2};	 // A<int>::A(int, int)
    
    	return 0;
    }
    
    

    auto占位的非类型模板形参

    详情
    #include <iostream>
    
    template <auto T>
    void foo() { std::cout << T << std::endl; }
    
    int main()
    {
    	foo<100>(); // foo<int>();
    	//foo<8.8>(); // foo<double>(); 这个是错误的,待研究
    
    	return 0;
    }
    
    

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

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

    详情
    #include <iostream>
    
    template <bool ok>
    constexpr void foo()
    {
    	//在编译期进行判断,if和else语句不生成代码
    	if constexpr (ok == true)
    	{
    		std::cout << "ok" << std::endl; //当ok为true时,下面的else块不生成汇编代码
    	}
    	else
    	{
    		std::cout << "not ok" << std::endl; //当ok为false时,上面的if块不生成汇编代码
    	}
    }
    
    int main()
    {
    	foo<true>();  //输出ok,并且汇编代码中只有 std::cout << "ok" << std::endl;
    	foo<false>(); //输出not ok,并且汇编代码中只有 std::cout << "not ok" << std::endl;
    
    	constexpr auto foo = [](int a, int b)
    	{ return a + b; };
    	static_assert(6 == foo(2, 3), "compile-time judge"); //static_assert关键字,用来做编译期间的断言,因此叫做静态断言
    	//compile_time_if_constexpr.cpp:24:18: error: static assertion failed: not compile-time
    	return 0;
    }
    
    

    内联变量

    扩展的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 <iostream>
    #include <unordered_map>
    #include <tuple>
    
    void c11_fun()
    {
    	auto student = std::make_tuple(std::string{"YongDu"}, 26, std::string{"man"});
    	std::string name;
    	size_t age;
    	std::string gender;
    	std::tie(name, age, gender) = student;
    	std::cout << name << ", " << age << ", " << gender << std::endl;
    }
    
    struct Student
    {
    	std::string name;
    	size_t age;
    };
    
    Student getStudent() { return {"dycc", 26}; }
    
    void c17_fun()
    {
    	auto student = std::make_tuple(std::string{"YongDu"}, 26, std::string{"man"});
    	auto [name, age, gender] = student;
    	std::cout << name << ", " << age << ", " << gender << std::endl;
    	// YongDu, 26, man
    
    	std::unordered_map<std::string, size_t> students;
    	students.emplace(std::make_pair("DuYong", 26));
    	students.emplace(std::make_pair("YongDu", 26));
    
    	for (auto &[name, age] : students)
    	{
    		std::cout << name << ", " << age << std::endl;
    	}
    
    	auto [_name, _age] = getStudent();
    	std::cout << _name << ", " << _age << std::endl;
    }
    
    int main()
    {
    	c11_fun();
    	c17_fun();
    
    	return 0;
    }
    
    

    执行结果:

    详情
    <!-- @include: ./res/structured_bindings.txt
    

    if,switch初始化

    详情
    #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;
    }
    
    

    简化的嵌套命名空间

    详情
    // C++17之前
    namespace A {
    namespace B {
    namespace C {
    void foo() {}
    } // namespace C
    } // namespace B
    } // namespace A
    
    // C++17
    namespace A::B::C {
    void foo() {}
    } // namespace A::B::C
    
    

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

    using std::cout, std::endl;
    

    新的求值顺序规则

    详情
    #include <iostream>
    #include <unordered_map>
    #include <cstdio>
    int a() { return std::puts("a"); }
    int b() { return std::puts("b"); }
    int c() { return std::puts("c"); }
    void z(int, int, int) {}
    
    int main()
    {
    	std::unordered_map<int, int> m_map;
    	m_map[0] = m_map.size(); // 此处不确定插入{0, 0},还是{0, 1}
    	std::cout << "m_map[0]:" << m_map[0] << std::endl;
    	
    	z(a(), b(), c());       // all 6 permutations of output are allowed
        return a() + b() + c(); // all 6 permutations of output are allowed
    }
    
    

    执行结果:

    详情
    <!-- @include: ./res/new_order_eva_rules.txt
    

    为了解决类似问题,C++17优化了求值顺序:

    • 后缀表达式从左到右求值,包括函数调用和成员选择表达式;
    • 赋值表达式从右向左求值,包括复合赋值;
    • 从左到右计算移位操作符的操作数。

    新增属性

    https://blog.csdn.net/weixin_42482896/article/details/118943564

    还有疑问

    • [[fallthrough]]: switch语句中跳到下一条语句,不需要break,让编译器忽略告警;
    • [[nodiscard]]: 所修饰的内容不可被忽略,主要用于修饰函数返回值:
    • [[maybe_unused]]
    详情
    #include <iostream>
    #include <unordered_map>
    #include <tuple>
    
    void fallthrough_fun()
    {
    
    	int i = 1;
    	int result;
    	switch (i)
    	{
    	case 0:
    		result = 1; // warning
    		std::cout<< "result=1"<<std::endl;
    	case 1:
    		result = 2;
    		[[fallthrough]]; // no warning
    		std::cout<< "result=2"<<std::endl;
    	default:
    		result = 0;
    		std::cout<< "result=0"<<std::endl;
    		break;
    	}
    }
    
    [[nodiscard]] auto nodiscard_fun(int a, int b) { return a + b; }
    
    int main()
    {
    	fallthrough_fun();
    	auto ret=nodiscard_fun(2, 3); // 放弃具有 "nodiscard" 属性的函数的返回值
    	std::cout<< ret<<std::endl;
    	[[maybe_unused]] int y = 2;
    	std::cout<< y<<std::endl;
    	return 0;
    }
    
    

    执行结果:

    [root@iZuf61kbf845xt6tz10abgZ c17]# ./new_attributes
    result=2
    result=0
    5
    2
    

    库相关

    • std::variant
      • C++17中提供了std::variant类型,意为多变的,可变的类型,类似于加强版的union,里面可以存放复合数据类型,且操作元素更为方便。
    • std::optional
      • 该类型主要用于简化函数返回值的判断
    • std::any
      • 在C++11中引入的auto自动推导类型变量大大方便了编程,但是auto变量一旦声明,该变量类型不可再改变。C++17中引入了std::any类型,该类型变量可以存储任何类型的值,也可以时刻改变它的类型,类似于python中的变量。
    • tuple
      • std::apply
        • 将tuple元组解包,并作为函数的传入参数。
      • std::make_from_tuple
        • 解包tuple作为构造函数参数构造对象。
      • std::apply
        • 将tuple元组解包,并作为函数的传入参数。
    • std::as_const
      • 将左值转化为const类型
    • std::shared_mutex
      • 读写锁相关
    详情
    #include <iostream>
    #include <unordered_map>
    #include <tuple>
    #include <optional>
    #include <string>
    #include <variant>
    #include <any>
    
    void variant_fun()
    {
    	std::cout << "variant_fun" << std::endl;
    	std::variant<int, std::string> var("hello");
    	std::cout << var.index() << std::endl;
    
    	var = 123;
    	std::cout << var.index() << std::endl;
    
    	try
    	{
    		var = "world";
    		std::string str = std::get<std::string>(var); // 通过类型获取
    		var = 3;
    		int i = std::get<0>(var); // 通过索引获取
    		std::cout << str << ", " << i << std::endl;
    	}
    	catch (...)
    	{
    	}
    }
    
    std::optional<int> StoI(const std::string &str)
    {
    	try
    	{
    		return std::stoi(str);
    	}
    	catch (...)
    	{
    		return std::nullopt;
    	}
    }
    
    void optional_fun()
    {
    	std::cout << "optional_fun" << std::endl;
    
    	std::string str{"1234"};
    	std::optional<int> result = StoI(str);
    	if (result)
    	{
    		std::cout << *result << std::endl;
    	}
    	else
    	{
    		std::cout << "StoI() error." << std::endl;
    	}
    }
    
    void any_fun()
    {
    	std::cout << "any_fun" << std::endl;
    
    	std::any a = 1;
    	std::cout << a.type().name() << ", " << std::any_cast<int>(a) << std::endl;
    
    	a = 2.2f;
    	std::cout << a.type().name() << ", " << std::any_cast<float>(a) << std::endl;
    
    	if (a.has_value())
    	{
    		std::cout << a.type().name() << std::endl;
    	}
    	a.reset();
    	if (a.has_value())
    	{
    		std::cout << a.type().name()<< std::endl;
    	}
    	a = std::string("hello");
    	std::cout << a.type().name() << ", " << std::any_cast<std::string>(a) << std::endl;
    }
    
    class Test
    {
    public:
    	Test(std::string name, size_t age) : _name(name), _age(age) { std::cout << "name: " << _name << ", age: " << _age << std::endl; }
    
    private:
    	std::string _name;
    	size_t _age;
    };
    
    int add(int a, int b) { return a + b; }
    
    void tuple_fun()
    {
    	std::cout << "tuple_fun" << std::endl;
    
    	//创建元组
    	std::tuple<int, char, double> tp(2, 'b', 8.5);
    	auto data0 = std::get<0>(tp); //获得里面的元素
    	auto data1 = std::get<1>(tp);
    	auto data2 = std::get<2>(tp);
    	auto tup1 = std::make_tuple("hello", 'a', 1.3);
    
    	//解包tuple作为构造函数参数构造对象
    	auto param = std::make_tuple("Jason", 25);
    	std::make_from_tuple<Test>(std::move(param));
    
    	// std::apply,将tuple元组解包,并作为函数的传入参数
    	auto add_lambda = [](auto a, auto b, auto c)
    	{ return a + b + c; };
    
    	std::cout << std::apply(add, std::pair(2, 3)) << std::endl;
    	std::cout << std::apply(add_lambda, std::tuple(2, 3, 4)) << std::endl;
    }
    
    void as_const_fun()
    {
    	std::cout << "as_const_fun" << std::endl;
    
    	std::string str{"hello world"};
    	std::cout << std::is_const<decltype(str)>::value << std::endl;
    
    	const std::string str_const = std::as_const(str);
    	std::cout << std::is_const<decltype(str_const)>::value << std::endl;
    }
    
    int main()
    {
    	variant_fun();
    	optional_fun();
    	any_fun();
    	tuple_fun();
    	as_const_fun();
    
    	return 0;
    }
    
    

    执行结果:

    [root@iZuf61kbf845xt6tz10abgZ c17]# ./std_fun                            
    variant_fun
    1
    0
    world, 3
    optional_fun
    1234
    any_fun
    i, 1
    f, 2.2
    f
    next
    NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, hello
    tuple_fun
    name: Jason, age: 25
    5
    9
    as_const_fun
    0
    1
    
    
    Last Updated:
    Contributors: klc407073648
    Prev
    C++ 新版本特性 - C++14
    Next
    C++ 新版本特性 - C++20