1. 构造函数模板推导
在C++17之前构造一个模板类对象需要指明类型:
1
| pair<int, double> p(1, 2,2);
|
从C++17开始,构造模板类对象可以根据值自行推导了,如下:
1 2 3
| pair p(1, 2,2); vector v = {1, 2, 3};
|
2. 结构化绑定
对于tuple、map、pair、数组、结构体类型,获取相应的值更方便了。看示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
std::tuple<int, double> func() { return std::tuple(1, 2.2); }
int main() { auto[i, d] = func(); cout << i << endl; cout << d << endl; }
|
结构化绑定不仅可以取对象值,还可以改变对象的值,使用引用即可。
1 2 3 4
| std::pair a(1, 2.3f); auto& [i, f] = a; i = 2; cout << a.first << endl;
|
3. if-switch语句初始化
C++17可以在if-switch语句中初始化变量,这样可以尽可能约束作用域,让代码更简洁,可能代码可读性略有下降,但是还好。
1 2 3 4
| if (int a = getValue(); a > 0) { cout << a; }
|
4. 内联变量
C++17前只有内联函数,现在新加了内联变量(头文件中也可以定义变量了),这样像以前静态成员变量必须在cpp中初始化的限制也没有了,成员变量可以直接在头文件也可以初始化了。
1 2 3 4 5 6 7 8 9 10
| struct A { static const int value; }; inline int const A::value = 10;
struct A { inline constexpr int value = 10; }
|
引入内联变量好处:
- 简化代码,允许在头文件中定义变量,无需实现文件中进行定义,减少代码重复和简化项目结构。
- 避免链接错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
extern int myVar;
#include "myvar.h" int myVar = 10;
#include "myvar.h" int myVar = 20;
inline int myVar = 10;
#include "myvar.h"
#include "myvar.h"
|
- 提高编译时性能,就如上面的示例,变量定义在头文件中,编译器在编译时可以直接把定义插入到使用它的代码中,而不需要进行链接,减少链接开销。
5. 折叠表达式
C++17引入了折叠表达式,在使用可变参数模板中更加方便了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std;
template <typename... Ts> auto sum(Ts... ts) { return (ts + ...); }
int main() { std::string a { "hello " }; std::string b { "world" }; cout << sum(a, b) << endl; }
|
6. constexpr lambda表达式
C++17之前lambda表达式只能在运行时计算,C++17引入了constexpr lambda表达式,可以在编译期就进行计算,提高运行效率。
1 2 3 4 5 6 7
| int main() { constexpr auto lamb = [](int n) { return n * n; }; if (lamb(3) == 9) { cout << "true" << endl; } }
|
注意:constexpr函数有如下限制:
函数体不能包含汇编语句、goto语句、label、try块、静态变量、线程局部存储、没有初始化的普通变量,不能动态分配内存,不能有new delete等,不能虚函数。
7. namespace嵌套
C++17开始标准化支持namespace嵌套的写法,更方便简洁了。
1 2 3 4 5 6 7 8 9 10 11 12
| namespace A { namespace B { namespace C { void func(); } } }
namespace A::B::C { void func();) }
|
8. __has_include预处理表达式
C++17引入了__has_include,是一个预处理器指令,用于在编译时检查某个头文件是否存在。
使用__has_include可以让我们在代码中动态地决定是否包含某个头文件,这对于跨平台开发和库的兼容性非常有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> int main() { if (__has_include("optional.h")) { std::cout << "Optional header is available." << std::endl; #include "optional.h" } else { std::cout << "Optional header is not available." << std::endl; } return 0; }
|
注:在vs2022上,语言集也设置到了c++17,自测报:error C3861: “__has_include”: 找不到标识符!
9. 支持lambda表达式用*this获取对象副本
C++17以前lambda只支持传递this引用,修改会导致本体也被修改。
1 2 3 4 5 6 7 8 9
| struct A { int a; void func() { auto f = [this] { cout << a << endl; }; f(); } };
|
所以现在扩展了,支持传递*this,传递的是对象的拷贝副本。
1 2 3 4 5 6 7 8 9
| struct A { int a; void func() { auto f = [*this] { cout << a << endl; }; f(); } };
|
10.新增Attribute
C++17又新增了3个属性:
- [[fallthrough]] 用在switch语句中,显示表明接下执行下一个case,而不是break跳出。不写break其实是同样的效果,它只是增加可读性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream>
int main() { int num = 1;
switch (num) { case 1: std::cout << "Number is 1." << std::endl; [[fallthrough]]; case 2: std::cout << "Number is 2." << std::endl; break; case 3: std::cout << "Number is 3." << std::endl; break; default: std::cout << "Number is not 1, 2, or 3." << std::endl; break; }
return 0; }
|
- [[nodiscard]] 修饰普通函数的返回值,用于向编译器发出警告,要求调用函数的返回值不被忽略。这个属性标记可以帮助开发人员避免忽略可能产生错误或潜在问题的函数返回值。只是一个编译警告,不会强制要求调用者使用函数的返回值。
1 2 3 4 5 6 7 8 9
| [[nodiscard]] int calculateSum(int a, int b) { return a + b; }
int main() { calculateSum(3, 4);
return 0; }
|
- [[maybe_unused]] 用于告诉编译器可以忽略某个实体(如变量、函数、参数等)未使用的警告。这个属性标记可以用于减少编译器产生的未使用实体的警告信息。
1 2 3 4 5 6 7 8 9
| [[maybe_unused]] void unusedFunction() { }
int main() { [[maybe_unused]] int unusedVariable = 5;
return 0; }
|
11. 字符串与基本类型相互转换
C++17新增了from_chars函数和to_chars函数,它们提供了一种高效的方式来解析字符串并将其转换为基本类型,或者将基本类型转换为字符序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <charconv> #include <iostream>
int main() { const char* str = "12345"; int value = 0; auto result = std::from_chars(str, str + std::strlen(str), value); if (result.ec == std::errc()) { std::cout << "Parsed value: " << value << std::endl; } else { std::cout << "Parsing failed." << std::endl; }
return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <charconv> #include <iostream>
int main() { int value = 12345; char buffer[20]; auto result = std::to_chars(buffer, buffer + sizeof(buffer), value); if (result.ec == std::errc()) { *result.ptr = '\0'; std::cout << "Converted value: " << buffer << std::endl; } else { std::cout << "Conversion failed." << std::endl; }
return 0; }
|
12. std::variant
std::variant是C++17引入的标准库模板类,它提供了一种类型安全的、高效的联合类型的实现。相比传统C语言的union,它更灵活,更安全,支持的类型更多。
使用std::variant时,需要在模板参数中指定可能的类型。例如,std::variant<int, double, std::string>表示一个可以容纳int、double和std::string类型中的一个值的std::variant对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <variant> #include <iostream> #include <string>
int main() { std::variant<int, double, std::string> var;
var = 42; std::cout << std::get<int>(var) << std::endl;
var = 3.14; std::cout << std::get<double>(var) << std::endl;
var = "Hello"; std::cout << std::get<std::string>(var) << std::endl;
return 0; }
|
13. std::optional
std::optional是C++17引入的标准库模板类,它提供了一种表示可能存在或不存在值的对象。在函数返回不存在的异常值情况时,配合std::nullopt使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <optional> #include <iostream>
std::optional<int> divide(int a, int b) { if (b != 0) { return a / b; } else { return std::nullopt; } }
int main() { std::optional<int> result = divide(10, 5); if (result) { std::cout << "Result: " << *result << std::endl; } else { std::cout << "Division by zero" << std::endl; }
return 0; }
|
14. std::any
std::any是C++17引入的标准库模板类,它提供了一种类型安全的机制来存储和访问任意类型的值。使用std::any时,可以将任意类型的值存储在std::any对象中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <any> #include <iostream> #include <string>
int main() { std::any value;
value = 42; std::cout << std::any_cast<int>(value) << std::endl;
value = 3.14; std::cout << std::any_cast<double>(value) << std::endl;
value = std::string("Hello"); std::cout << std::any_cast<std::string>(value) << std::endl;
return 0; }
|
15. std::apply
std::apply是C++17引入的标准库函数,用于将参数包展开并应用到函数中。它提供了一种方便的方式来调用函数,将参数包作为函数的参数传递,而不需要显式地编写参数列表。(使用std::tuple打包参数给普通函数传参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> #include <tuple> #include <functional>
void print_values(int a, float b, const std::string& c) { std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl; }
int main() { std::tuple<int, float, std::string> values(42, 3.14, "Hello");
std::apply(print_values, values);
return 0; }
|
16. std::make_from_tuple
std::make_from_tuple是C++17引入的标准库函数,std::make_from_tuple会使用std::tuple中的元素作为某个类的构造函数的参数,构造这个对象并返回。(使用std::tuple打包参数给构造函数传参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <tuple> #include <string>
struct Person { std::string name; int age;
Person(const std::string& name, int age) : name(name), age(age) {}
void print_info() { std::cout << "Name: " << name << ", Age: " << age << std::endl; } };
int main() { std::tuple<std::string, int> person_info("Alice", 25);
auto person = std::make_from_tuple<Person>(person_info);
person.print_info();
return 0; }
|
17. std::string_veiw
std::string_view是C++17引入的标准库类型,它提供了一种轻量级的方式来操作字符串数据,而无需复制字符串内容。它本质上是一个指向字符串数据的指针和长度的组合,它并不拥有字符串的存储空间。因此,它适用于需要对字符串进行读取操作而不需要修改字符串的场景。
这样传递字符串参数使用string_view对象传递,就可以避免字符串拷贝操作了,提高运行效率。(其实和传递&string引用差不多,只是另一种写法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <iostream> #include <string_view>
void print_string_view(std::string_view str) { std::cout << "String view: " << str << std::endl; std::cout << "Length: " << str.length() << std::endl; }
int main() { std::string str = "Hello, world!"; std::string_view str_view(str);
print_string_view(str_view);
return 0; }
|
18. as_const
std::as_const是一个 C++17 引入的函数模板,用于将一个对象转换为 const 引用。它的作用是确保无法通过该 const 引用对对象进行修改,即使原本对象是非 const 的。
1 2
| std::string str = "hello world"; const std::string& constStr = std::as_const(str);
|
19. file_system
std::filesystem 是 C++17 引入的标准库,用于操作文件系统和文件路径。它提供了一组类和函数,用于处理文件、目录、路径等相关操作。
要使用 std::filesystem,需要包含 <filesystem> 头文件,并使用 std::filesystem 命名空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream> #include <filesystem>
namespace fs = std::filesystem;
int main() { fs::path filePath = "path/to/file.txt"; if (fs::exists(filePath)) { std::cout << "File exists." << std::endl; } else { std::cout << "File does not exist." << std::endl; }
fs::path dirPath = "path/to/directory"; if (fs::create_directory(dirPath)) { std::cout << "Directory created." << std::endl; } else { std::cout << "Failed to create directory." << std::endl; }
fs::path directory = "path/to/directory"; for (const auto& entry : fs::directory_iterator(directory)) { std::cout << entry.path() << std::endl; }
fs::path file = "path/to/file.txt"; if (fs::exists(file) && fs::is_regular_file(file)) { std::cout << "File size: " << fs::file_size(file) << " bytes." << std::endl; }
fs::path fileToDelete = "path/to/file.txt"; if (fs::exists(fileToDelete) && fs::is_regular_file(fileToDelete)) { if (fs::remove(fileToDelete)) { std::cout << "File deleted." << std::endl; } else { std::cout << "Failed to delete file." << std::endl; } }
return 0; }
|
20. std::shared_mutex
std::shared_mutex 是 C++14 引入的标准库,用于实现读写锁(Read-Write Lock)的功能。它提供了一种机制,允许多个线程同时读取共享数据,但只允许一个线程写入共享数据。
它和c++14的std::shared_timed_mutex 一样,只是少了超时功能,更加简洁简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <shared_mutex> #include <thread>
std::shared_mutex mutex; int sharedData = 0;
void writerThread() { for (int i = 0; i < 5; ++i) { std::unique_lock<std::shared_mutex> lock(mutex); ++sharedData; std::cout << "Writer thread: wrote " << sharedData << std::endl; } }
void readerThread() { for (int i = 0; i < 5; ++i) { std::shared_lock<std::shared_mutex> lock(mutex); std::cout << "Reader thread: read " << sharedData << std::endl; } }
int main() { std::thread writer(writerThread); std::thread reader(readerThread);
writer.join(); reader.join();
return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Writer thread: wrote 1 Writer thread: wrote 2 Writer thread: wrote 3 Writer thread: wrote 4 Writer thread: wrote 5 Reader thread: read 5 Reader thread: read 5 Reader thread: read 5 Reader thread: read 5 Reader thread: read 5 或者======================= Reader thread: read 0 Reader thread: read 0 Reader thread: read 0 Reader thread: read 0 Reader thread: read 0 Writer thread: wrote 1 Writer thread: wrote 2 Writer thread: wrote 3 Writer thread: wrote 4 Writer thread: wrote 5
|