C++ 17概述
引言
C++17是C++语言的第五个主要标准,于2017年正式发布。它在C++11和C++14的基础上进一步扩展了语言功能,引入了许多新特性,使代码更加简洁、安全和高效。对于初学者来说,了解C++17带来的新特性不仅能够编写更现代化的代码,还能更好地理解现有的C++项目。
本文将概述C++17的主要特性,并通过简单易懂的例子展示它们的用法和优势。
C++ 17核心语言特性
结构化绑定
结构化绑定允许我们从元组、数组或结构体中一次性提取多个值,并将它们绑定到变量上。
#include <iostream>
#include <tuple>
int main() {
// 从元组中提取值
std::tuple<int, double, std::string> myTuple{1, 2.3, "Hello"};
auto [id, value, message] = myTuple;
std::cout << "ID: " << id << std::endl;
std::cout << "Value: " << value << std::endl;
std::cout << "Message: " << message << std::endl;
// 从结构体中提取值
struct Person {
std::string name;
int age;
};
Person person{"Alice", 30};
auto [name, age] = person;
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
return 0;
}
输出:
ID: 1
Value: 2.3
Message: Hello
Name: Alice
Age: 30
if和switch语句中的初始化器
C++17允许在if和switch语句中声明变量,这些变量的作用域仅限于该语句块。
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> ages{
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
// 在if语句中初始化变量
if (auto it = ages.find("Bob"); it != ages.end()) {
std::cout << "Bob's age is " << it->second << std::endl;
} else {
std::cout << "Bob not found" << std::endl;
}
// it 变量在这里已经超出作用域
// 在switch语句中初始化变量
switch (int value = 2; value) {
case 1:
std::cout << "Value is 1" << std::endl;
break;
case 2:
std::cout << "Value is 2" << std::endl;
break;
default:
std::cout << "Unknown value" << std::endl;
}
return 0;
}
输出:
Bob's age is 30
Value is 2
内联变量
C++17引入了内联变量的概念,允许在头文件中定义全局变量,而不会导致多重定义错误。
// header.h
#ifndef HEADER_H
#define HEADER_H
#include <string>
inline int globalCounter = 0;
inline const std::string appName = "My Application";
class MyClass {
public:
static inline int instanceCount = 0;
};
#endif
这样,你可以在多个源文件中包含同一个头文件,而不会引起链接错误。
constexpr if
constexpr if
允许在编译时根据条件选择性地编译代码。
#include <iostream>
#include <type_traits>
template<typename T>
void print_value(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integral value: " << value << std::endl;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Floating point value: " << value << std::endl;
} else {
std::cout << "Other type value" << std::endl;
}
}
int main() {
print_value(42); // 整型
print_value(3.14); // 浮点型
print_value("Hello"); // 其他类型
return 0;
}
输出:
Integral value: 42
Floating point value: 3.14
Other type value
折叠表达式
折叠表达式简化了对参数包的操作,使可变参数模板更加易用。
#include <iostream>
// 求和的折叠表达式
template<typename... Args>
auto sum(Args... args) {
return (... + args); // 一元左折叠
}
// 打印所有参数的折叠表达式
template<typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...); // 一元右折叠
}
int main() {
std::cout << "Sum: " << sum(1, 2, 3, 4, 5) << std::endl;
std::cout << "Values: ";
print(10, 20, "hello", 3.14);
std::cout << std::endl;
return 0;
}
输出:
Sum: 15
Values: 10 20 hello 3.14
C++ 17标准库特性
std::optional
std::optional
是一个模板类,可以容纳一个值或者不包含任何值,非常适合表示可能不存在的值。
#include <iostream>
#include <optional>
#include <string>
std::optional<std::string> get_nickname(const std::string& name) {
if (name == "Robert") {
return "Bob";
} else if (name == "William") {
return "Bill";
} else {
return std::nullopt; // 没有昵称
}
}
int main() {
auto nick1 = get_nickname("Robert");
if (nick1) {
std::cout << "Robert's nickname is " << *nick1 << std::endl;
}
auto nick2 = get_nickname("Alice");
if (nick2) {
std::cout << "Alice's nickname is " << *nick2 << std::endl;
} else {
std::cout << "Alice doesn't have a nickname" << std::endl;
}
return 0;
}
输出:
Robert's nickname is Bob
Alice doesn't have a nickname
std::variant
std::variant
是一个类型安全的联合体,可以存储多种类型中的一种。
#include <iostream>
#include <variant>
#include <string>
int main() {
std::variant<int, double, std::string> data;
data = 42;
std::cout << "Int value: " << std::get<int>(data) << std::endl;
data = 3.14;
std::cout << "Double value: " << std::get<double>(data) << std::endl;
data = "Hello, C++17";
std::cout << "String value: " << std::get<std::string>(data) << std::endl;
// 检查变体当前持有的类型
if (std::holds_alternative<std::string>(data)) {
std::cout << "data currently holds a string" << std::endl;
}
return 0;
}
输出:
Int value: 42
Double value: 3.14
String value: Hello, C++17
data currently holds a string
std::any
std::any
可以存储任意类型的单个值。
#include <iostream>
#include <any>
#include <string>
#include <vector>
int main() {
std::any a = 42;
std::cout << std::any_cast<int>(a) << std::endl;
a = 3.14;
std::cout << std::any_cast<double>(a) << std::endl;
a = std::string("Hello, C++17");
std::cout << std::any_cast<std::string>(a) << std::endl;
// 可以存储容器
a = std::vector<int>{1, 2, 3, 4, 5};
for (int i : std::any_cast<std::vector<int>>(a)) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
输出:
42
3.14
Hello, C++17
1 2 3 4 5
std::string_view
std::string_view
提供了一个字符串的只读视图,不需要拷贝字符串内容,有助于提高性能。
#include <iostream>
#include <string>
#include <string_view>
void print_string(std::string_view sv) {
std::cout << "String view: " << sv << std::endl;
}
int main() {
std::string str = "Hello, C++17";
// 不需要拷贝字符串
print_string(str);
// 可以直接使用字符串字面量
print_string("Direct literal");
// 可以表示字符串的一部分,而不需要创建新字符串
print_string(str.substr(0, 5));
return 0;
}
输出:
String view: Hello, C++17
String view: Direct literal
String view: Hello
并行算法
C++17在STL中添加了对并行执行算法的支持,可以通过执行策略来控制算法的执行方式。
#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>
#include <chrono>
int main() {
std::vector<int> v(10000000);
// 初始化向量
for (int i = 0; i < v.size(); ++i) {
v[i] = i;
}
// 测量顺序排序时间
auto start = std::chrono::high_resolution_clock::now();
std::sort(v.begin(), v.end());
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> seq_time = end - start;
// 随机打乱
std::random_shuffle(v.begin(), v.end());
// 测量并行排序时间
start = std::chrono::high_resolution_clock::now();
std::sort(std::execution::par, v.begin(), v.end());
end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> par_time = end - start;
std::cout << "Sequential sort: " << seq_time.count() << " seconds" << std::endl;
std::cout << "Parallel sort: " << par_time.count() << " seconds" << std::endl;
return 0;
}
要使用并行算法,你可能需要链接特定的库,如Intel TBB(线程构建块)或其他并行库。
文件系统库
C++17引入了标准文件系统库,提供了一套跨平台的文件和目录操作接口。
#include <iostream>
#include <filesystem>
#include <string>
namespace fs = std::filesystem;
void explore_directory(const fs::path& path) {
if (fs::exists(path) && fs::is_directory(path)) {
std::cout << "Directory: " << path << std::endl;
for (const auto& entry : fs::directory_iterator(path)) {
auto filename = entry.path().filename().string();
if (fs::is_regular_file(entry.status())) {
std::cout << " File: " << filename
<< " (" << fs::file_size(entry) << " bytes)" << std::endl;
} else if (fs::is_directory(entry.status())) {
std::cout << " Subdirectory: " << filename << std::endl;
}
}
} else {
std::cout << "Path does not exist or is not a directory" << std::endl;
}
}
int main() {
explore_directory("."); // 探索当前目录
// 创建目录
fs::create_directory("testdir");
std::cout << "\nAfter creating testdir:" << std::endl;
explore_directory(".");
// 删除目录
fs::remove("testdir");
return 0;
}
实际应用场景
使用std::optional进行错误处理
#include <iostream>
#include <optional>
#include <string>
#include <fstream>
std::optional<std::string> read_file_content(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
return std::nullopt;
}
std::string content;
std::string line;
while (std::getline(file, line)) {
content += line + '\n';
}
return content;
}
int main() {
auto content = read_file_content("example.txt");
if (content) {
std::cout << "File content:\n" << *content << std::endl;
} else {
std::cout << "Failed to read the file." << std::endl;
}
return 0;
}
使用结构化绑定简化Map操作
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> user_scores = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78}
};
// 添加或更新分数
if (auto [iter, inserted] = user_scores.insert_or_assign("David", 88); inserted) {
std::cout << "Added new user David with score 88" << std::endl;
} else {
std::cout << "Updated existing user David's score to 88" << std::endl;
}
// 遍历map,使用结构化绑定
for (const auto& [name, score] : user_scores) {
std::cout << name << ": " << score << std::endl;
}
return 0;
}
输出:
Added new user David with score 88
Alice: 85
Bob: 92
Charlie: 78
David: 88
使用文件系统库创建简单的文件浏览器
#include <iostream>
#include <filesystem>
#include <vector>
#include <algorithm>
namespace fs = std::filesystem;
void list_directory(const fs::path& path, int level = 0) {
if (!fs::exists(path) || !fs::is_directory(path)) {
std::cout << "Invalid directory path" << std::endl;
return;
}
std::string indent(level * 2, ' ');
std::vector<fs::directory_entry> entries;
// 收集条目
for (const auto& entry : fs::directory_iterator(path)) {
entries.push_back(entry);
}
// 排序:先目录,后文件
std::sort(entries.begin(), entries.end(),
[](const fs::directory_entry& a, const fs::directory_entry& b) {
if (fs::is_directory(a) && !fs::is_directory(b)) return true;
if (!fs::is_directory(a) && fs::is_directory(b)) return false;
return a.path().filename() < b.path().filename();
});
// 显示条目
for (const auto& entry : entries) {
auto filename = entry.path().filename().string();
if (fs::is_directory(entry)) {
std::cout << indent << "[DIR] " << filename << std::endl;
// 递归显示子目录内容(限制递归深度为2)
if (level < 2) {
list_directory(entry.path(), level + 1);
}
} else {
auto size = fs::file_size(entry);
std::cout << indent << "[FILE] " << filename << " (" << size << " bytes)" << std::endl;
}
}
}
int main() {
std::string path_str;
std::cout << "Enter directory path to explore (default is current directory): ";
std::getline(std::cin, path_str);
fs::path path = path_str.empty() ? fs::current_path() : path_str;
std::cout << "Exploring: " << fs::absolute(path) << std::endl << std::endl;
list_directory(path);
return 0;
}
总结
C++17为C++语言带来了许多实用的新特性,包括:
-
核心语言改进:
- 结构化绑定
- if/switch初始化器
- 内联变量
- constexpr if
- 折叠表达式
-
标准库增强:
- std::optional
- std::variant
- std::any
- std::string_view
- 并行算法
- 文件系统库
这些特性使得C++代码编写更加简洁、安全和高效,尤其是对于初学者来说,利用这些现代C++特性可以大大提高编程效率和代码质量。
练习
-
编写一个程序,使用
std::optional
来表示可能为空的用户数据,并处理不同情况。 -
创建一个使用
std::variant
存储不同类型的配置值(整数、浮点数、字符串)的配置系统。 -
使用结构化绑定和
std::map
实现一个简单的通讯录应用,支持添加、查询和删除联系人。 -
利用C++17文件系统库编写一个程序,统计指定目录中各类型文件(按扩展名)的数量和总大小。
学习资源
- ISO C++17标准文档
- C++17标准库头文件参考
- Bjarne Stroustrup的《C++程序设计原理与实践》(更新版本涵盖C++17)
- 在线资源:CPPReference.com中的C++17特性页面
- 各种C++编译器文档中的C++17支持说明
这些资源将帮助你更深入地了解C++17的各个特性,并在实践中加以应用。