# 1、结构化绑定

#CPP 新特性 #CPP17
C++17 引入了一项非常强大的特性,称为结构化绑定(Structured Bindings),它允许你将复杂的数据结构(如 std::tuple 、数组或用户定义的类型)的成员绑定到命名变量,从而使代码更加清晰和易于理解。结构化绑定的主要目的是让代码更加直观,减少手动提取和命名数据的需要。

以下是结构化绑定的主要特点和用法:
自动解包:结构化绑定允许您将数据结构中的元素自动解包到单独的变量中,而无需手动提取元素。
更具可读性:通过为数据结构的各个成员指定名称,代码变得更加自解释和可读。
适用于多种数据结构:结构化绑定不仅适用于 STL 容器,还适用于用户定义的类型和数组。
更少的样板代码:它减少了编写样板代码的需要,特别是在处理多元组或类似结构的数据时。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 使用结构化绑定处理用户定义的类型
#include <iostream>
#include <string>

struct Person {
std::string name;
int age;
};

int main() {
Person person = {"Alice", 30};

// 使用结构化绑定
auto [name, age] = person;

std::cout << "姓名: " << name << std::endl;
std::cout << "年龄: " << age << std::endl;

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用结构化绑定处理std::tuple
#include <iostream>
#include <tuple>

int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");

// 使用结构化绑定
auto [intValue, doubleValue, stringValue] = myTuple;

std::cout << "整数值: " << intValue << std::endl;
std::cout << "双精度值: " << doubleValue << std::endl;
std::cout << "字符串值: " << stringValue << std::endl;

return 0;
}

# 2、std::variant

#CPP 新特性 #CPP17
std::variant 是 C++17 引入的标准库类型,用于表示可以取多种类型中的一种的值。它是一种类似于联合(union)的类型,但相比联合更加类型安全,提供了更多的功能。

下面是一个简单的示例,演示了如何使用 std::variant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <variant>
#include <iostream>

int main() {
std::variant<int, double, std::string> myVariant;

myVariant = 42; // 存储整数
std::cout << std::get<int>(myVariant) << std::endl;

myVariant = 3.14; // 存储浮点数
std::cout << std::get<double>(myVariant) << std::endl;

myVariant = "Hello, Variant!"; // 存储字符串
std::cout << std::get<std::string>(myVariant) << std::endl;

return 0;
}

这个例子中, std::variant 可以存储 intdoublestd::string 中的一种类型的值。在运行时,可以根据当前存储的类型使用 std::get 进行访问。

std::variant 具有以下特点:

  1. 多类型支持: std::variant 可以存储多个不同类型的值,但在任意时刻只能包含其中一种类型的值。

  2. 类型安全: 使用 std::variant 可以避免使用联合时可能引起的类型错误。在编译时,编译器会检查对 std::variant 的访问,确保访问的类型是正确的。

  3. 替代联合和指针: 在过去,人们可能使用联合或指针来实现一种变体的效果。但是,这些方法可能不够类型安全,容易引入错误。 std::variant 提供了更好的类型安全性,同时也避免了使用指针带来的一些问题。

  4. 异常安全: std::variant 提供了异常安全的访问方法,避免了在异常情况下的不确定行为。

  5. 访问器函数: std::getstd::get_if 函数用于访问 std::variant 中的值。 std::get 在访问时要求知道值的确切类型,而 std::get_if 则返回指向值的指针,允许进行类型检查。

  6. 访问者模式: std::visit 允许通过访问者模式访问 std::variant 中的值,这是一种在多种类型之间进行分发操作的方法,这样的设计使得可以轻松地在多种类型之间进行分发操作。

对于 variant,可以有这么一些应用场景,比如函数的返回值可能由于条件的不同而返回不同类型,比如求根公式的结果可能有一个根、两个根或者没有根,那么就可以用:

1
using Roots = std::variant<double, std::pair<double, double>, nullptr_t>;

另外一个场景是多态,这里不展开。

# 3、std::string_view

#CPP 新特性 #CPP17
std::string_view 是 C++17 标准引入的一种轻量级字符串视图(String View)类型。它允许你在不拷贝字符串的情况下引用现有的字符串数据。这对于传递字符串参数、字符串操作和提高性能都是有益的。

1
#include <string_view>

std::string_view 本身不拥有字符串的内存,它只是一个对现有字符串数据的引用。因此,在使用期间需要确保原始字符串的生命周期长于字符串视图的生命周期。

std::string_view 提供了一种轻便且高效的方式来处理字符串数据,特别是在函数参数传递和字符串操作时。当你需要引用而非拷贝字符串数据时,可以考虑使用它。

优势:

  1. 参数传递的轻量化
    • 当你需要传递字符串参数但不需要拷贝整个字符串时, std::string_view 是一种很好的选择。这可以避免不必要的内存拷贝开销,对于提高性能和减少内存开销是有益的。
  2. 字符串切片和子串操作
    • std::string_view 提供了方便的子串操作,允许你在不创建新字符串的情况下访问原始字符串的一部分。
      1
      2
      std::string fullString = "C++ String View";
      std::string_view substr = std::string_view(fullString).substr(3, 6);
  3. 性能提升
    • 使用 std::string_view 可以避免不必要的内存分配和拷贝,从而提高程序的性能。特别是在处理大量字符串数据或频繁进行字符串操作时,这种性能提升可能非常显著。

# 4、if 初始化语句

#CPP 新特性 #CPP17
允许在 if 语句中初始化变量。

1
2
3
4
5
if (auto result = someFunction(); result > 0) {
// 处理正结果
} else {
// 处理负结果或零
}

# 5、属性说明符

#CPP 新特性 #CPP17

  1. [[nodiscard]] :这是一个属性,用于告诉编译器,函数的返回值不应被忽略。如果带有 [[nodiscard]] 属性的函数的返回值没有被使用,编译器可能会发出警告。这对于防止程序员忽略有返回值的函数的结果很有用。

  2. [[maybe_unused]] :这是一个属性,用于告诉编译器,标记的实体(例如变量、函数参数、函数等)可能未被使用,但是不应触发警告。这对于编写一些代码,其中某些实体可能在特定情况下未被使用很有用,但不希望因此触发警告。

  3. [[fallthrough]] :这是一个标签,用于在 C++17 中实现 fallthrough 语句。在 switch 语句中,如果你想要有一个意图的 “穿透” 效果,即使不写 break 语句,编译器也不会生成警告。 [[fallthrough]] 标签可以在某个 case 中明确表示穿透到下一个 case ,从而避免编译器生成不必要的警告。

对于 nodiscard,除了可以修饰函数之外,还可以用于修饰类:

在 C++17 及其后的标准中, [[nodiscard]] 属性(attribute)被引入,用于指示特定函数或返回类型的结果不应被忽略。当应用于类时,它会要求所有返回该类类型的对象必须被使用。如果忽略了这样的返回值,编译器会生成一个警告。这对于提示程序员注意某些操作的结果是非常有用的,避免忽略可能的重要结果。

# 目的和优点
  1. 防止资源泄漏:对于表示资源管理的类,如线程池、文件句柄或智能指针,忽略返回值可能导致资源未正确管理。 [[nodiscard]] 可以提醒程序员必须处理这些对象。
  2. 强制检查操作结果:有些操作可能失败,返回一个错误对象或状态对象。如果忽略了这些返回值,程序可能无法正确处理错误情况。
  3. 提高代码安全性:通过强制检查返回值,可以减少因忽略重要操作结果而引入的潜在错误。
# 具体应用场景

使用 [[nodiscard]] 属性的典型场景包括但不限于:

  1. 资源管理类:如线程池、文件操作类、网络连接类等。
  2. 错误处理类:如表示错误状态或异常信息的类。
  3. 智能指针:如自定义的智能指针实现。
# 总结

在类定义中使用 [[nodiscard]] 属性可以帮助程序员避免忽略重要的返回值,从而提高代码的健壮性和安全性。它是一种编译时提示机制,帮助捕捉那些因忽略返回值而可能导致的错误或未预期的行为。在使用 [[nodiscard]] 时,需要确保返回的对象确实需要被使用,并且使用返回值是合理且必要的。

更新于

请我喝杯咖啡吧~

Rick 微信支付

微信支付

Rick 支付宝

支付宝