C++17 是 C++ 编程语言的一个重要版本,它在 C++14 的基础上引入了许多新特性,旨在简化代码、提高性能并增强语言的表达能力。本教程将全面介绍 C++17 的基础语法,帮助你快速掌握这一现代 C++ 版本的核心特性。
一、C++17 新特性概述
C++17 引入了许多新特性,可以分为以下几类:
- 结构化绑定(Structured Bindings)
- if 和 switch 中的初始化语句
- 内联变量(Inline Variables)
- 折叠表达式(Fold Expressions)
-
std::optional
和std::variant
-
std::string_view
- 并行算法(Parallel Algorithms)
- 文件系统库(Filesystem Library)
- 其他改进和优化
下面我们将逐一详细介绍这些特性。
二、结构化绑定(Structured Bindings)
结构化绑定允许你将一个复合类型(如 std::pair
、std::tuple
或数组)的成员绑定到多个变量上,从而简化代码。
示例代码
#include <iostream>
#include <tuple>
int main() {
// 使用 std::tuple
std::tuple<int, double, std::string> t = {1, 3.14, "Hello"};
auto [id, pi, message] = t; // 结构化绑定
std::cout << "ID: " << id << ", Pi: " << pi << ", Message: " << message << std::endl;
// 使用数组
int arr[3] = {10, 20, 30};
auto [a, b, c] = arr; // 结构化绑定
std::cout << "Array elements: "<< a << ", "<< b << ", "<< c << std::endl;
return 0;
}
解释:
- 结构化绑定使得我们可以直接从
std::tuple
或数组中提取值并赋给多个变量,而不需要手动访问每个成员。 - 这种语法糖让代码更加简洁易读。
三、if 和 switch 中的初始化语句
C++17 允许在 if
和 switch
语句中进行变量声明和初始化,这可以减少代码的嵌套层次,提高可读性。
示例代码
#include <iostream>
#include <optional>
std::optional<int> findValue(const std::vector<int>& vec, int target) {
for (int v : vec) {
if (v == target) return v;
}
return std::nullopt;
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// if 中的初始化语句
if (auto value = findValue(numbers, 3); value.has_value()) {
std::cout << "Found: " << *value << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
// switch 中的初始化语句(C++17 不支持直接在 switch 中初始化,但可以通过作用域实现)
{
auto value = findValue(numbers, 6);
switch (value.has_value()) {
case true:
std::cout << "Found: " << *value << std::endl;
break;
case false:
std::cout << "Not found" << std::endl;
break;
}
}
return 0;
}
解释:
- 在
if
语句中,我们可以在条件部分声明并初始化一个变量value
,这使得代码更加紧凑。 - 虽然
switch
语句不支持直接在条件部分初始化变量,但我们可以通过作用域来模拟类似的效果。
四、内联变量(Inline Variables)
内联变量允许你在头文件中定义变量,而不会导致多重定义错误。这在 C++17 之前需要使用 static
或在源文件中定义变量。
示例代码
// my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H
#include <string>
inline const std::string appName = "MyApp";
#endif // MY_HEADER_H
// main.cpp
#include <iostream>
#include "my_header.h"
int main() {
std::cout << "Application Name: " << appName << std::endl;
return 0;
}
解释:
- 在 C++17 之前,如果你在头文件中定义一个变量,然后在多个源文件中包含该头文件,会导致链接错误(多重定义)。
- 使用
inline
关键字,你可以在头文件中安全地定义变量,编译器会确保变量只被定义一次。
五、折叠表达式(Fold Expressions)
折叠表达式简化了可变参数模板的展开操作,使得代码更加简洁。
示例代码
#include <iostream>
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 折叠表达式
}
int main() {
std::cout << "Sum of 1, 2, 3, 4, 5: " << sum(1, 2, 3, 4, 5) << std::endl;
return 0;
}
解释:
- 折叠表达式
(args + ...)
将可变参数args
展开为一个加法表达式。 - 这种语法糖使得处理可变参数模板变得更加简单。
六、std::optional
和 std::variant
1. std::optional
std::optional
用于表示一个可能包含值或不包含值的对象,避免了使用特殊值(如 -1
、nullptr
)来表示无效状态。
示例代码
#include <iostream>
#include <optional>
std::optional<int> divide(int a, int b) {
if (b == 0) {
return std::nullopt; // 表示没有值
}
return a / b;
}
int main() {
auto result = divide(10, 2);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Division by zero" << std::endl;
}
return 0;
}
解释:
std::optional
提供了一种类型安全的方式来表示可能缺失的值。- 使用
std::nullopt
表示没有值,通过has_value()
或直接检查optional
对象是否为空来判断是否有值。
2. std::variant
std::variant
是一个类型安全的联合体,可以存储多种类型的值,类似于 union
,但更加安全。
示例代码
#include <iostream>
#include <variant>
#include <string>
int main() {
std::variant<int, double, std::string> v;
v = 42;
std::cout << "Holds int: " << std::get<int>(v) << std::endl;
v = 3.14;
std::cout << "Holds double: " << std::get<double>(v) << std::endl;
v = "Hello";
std::cout << "Holds string: " << std::get<std::string>(v) << std::endl;
return 0;
}
解释:
std::variant
可以存储多种类型的值,但同一时间只能存储其中一种类型。- 使用
std::get<T>
来获取存储的值,如果类型不匹配会抛出异常。
七、std::string_view
std::string_view
提供了一个轻量级的、非拥有的字符串视图,避免了不必要的字符串拷贝。
示例代码
#include <iostream>
#include <string_view>
void printString(std::string_view sv) {
std::cout << sv << std::endl;
}
int main() {
std::string str = "Hello, World!";
printString(str); // 传递 std::string
printString("Direct string literal"); // 传递字符串字面量
return 0;
}
解释:
std::string_view
不拥有字符串数据,只是提供一个视图,因此避免了拷贝开销。- 可以接受
std::string
和字符串字面量作为参数。
八、并行算法(Parallel Algorithms)
C++17 引入了并行版本的 STL 算法,可以显著提高计算密集型任务的性能。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>
int main() {
std::vector<int> vec(1000000, 1);
// 并行执行 std::for_each
std::for_each(std::execution::par, vec.begin(), vec.end(), [](int& n) {
n += 1;
});
// 并行执行 std::sort
std::sort(std::execution::par, vec.begin(), vec.end());
std::cout << "First element: " << vec[0] << std::endl;
return 0;
}
解释:
- 使用
std::execution::par
策略,算法会并行执行,充分利用多核处理器。 - 注意:并行算法需要编译器支持,并且可能需要链接额外的库(如 TBB)。
九、文件系统库(Filesystem Library)
C++17 引入了 <filesystem>
库,提供了跨平台的文件系统操作功能。
示例代码
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path p = "/path/to/directory";
if (fs::exists(p)) {
std::cout << "Path exists" << std::endl;
if (fs::is_directory(p)) {
std::cout << "It's a directory" << std::endl;
for (const auto& entry : fs::directory_iterator(p)) {
std::cout << entry.path().filename() << std::endl;
}
}
} else {
std::cout << "Path does not exist" << std::endl;
}
return 0;
}
解释:
<filesystem>
库提供了跨平台的文件系统操作,如检查文件是否存在、遍历目录等。- 使用
namespace fs = std::filesystem
可以简化代码。
十、其他改进
1. 结构化绑定与 std::pair
和 std::tuple
#include <iostream>
#include <tuple>
int main() {
auto [x, y, z] = std::make_tuple(1, 2.5, "Hello");
std::cout<< x << ", "<< y << ", "<< z << std::endl;
return 0;
}
2. if
和 switch
中的初始化语句
#include <iostream>
#include <optional>
std::optional<int> findValue(const std::vector<int>& vec, int target) {
for (int v : vec) {
if (v == target) return v;
}
return std::nullopt;
}
int main() {
std::vector<int> v = {1, 2, 3};
if (auto it = findValue(v, 2); it) {
std::cout << "Found: " << *it << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
return 0;
}
3. [[nodiscard]]
属性
#include <iostream>
[[nodiscard]] int computeValue() {
return 42;
}
int main() {
// 编译器警告:忽略返回值
computeValue();
// 正确用法
int value = computeValue();
std::cout << "Value: " << value << std::endl;
return 0;
}
解释:
[[nodiscard]]
属性告诉编译器,函数的返回值不应该被忽略。- 如果调用者忽略了返回值,编译器会发出警告。