【C++八股-第12期】C++17 新特性

感谢花花,你必Offer

提纲:

👉 八股:

  1. 构造函数模板推导 CTAD

  2. 结构化绑定(Structured Bindings)

  3. 条件语句初始化(Condition Variable Initialization)

  4. 内联变量(inline variables)

  5. std::variant

  6. std::optional

  7. std::any

  8. std::apply

1. 构造函数模板推导 CTAD

  • 在C++17前构造一个模板类对象需要指明类型:
pair<int, double> p(1, 2.2); // c++17之前
  • C++17就不需要特殊指定,直接可以推导出类型
pair p(1, 2.2); // c++17 自动推导
vector v = {1, 2, 3}; // c++17

2. 结构化绑定(Structured Bindings)

C++17 引入了结构化绑定(Structured Bindings),它是一种语法糖,可以让你更方便地将一个结构或元组中的多个成员绑定到多个变量上,从而提高代码的可读性和简洁性。结构化绑定可以用于解构 std::tuplestd::pair、数组以及具有适当成员函数的类对象。

基本语法

auto [var1, var2, var3] = expression;

其中 expression 是一个返回多个值的表达式,比如 std::tuplestd::pair、数组或具有适当 get 方法的类对象。

示例:

#include <iostream>
#include <tuple>

std::tuple<int, double, std::string> getTuple() {
    return std::make_tuple(42, 3.14, "hello");
}

int main() {
    auto [i, d, s] = getTuple();
    std::cout << "i: " << i << ", d: " << d << ", s: " << s << std::endl;
    return 0;
}

---
out:
i: 42, d: 3.14, s: hello

3. 条件语句初始化(Condition Variable Initialization)

C++17 引入了一项名为条件语句初始化(Condition Variable Initialization)的新特性,这项特性允许在 if 和 switch 语句中进行变量初始化。这使得代码更加简洁和局部化,减少了变量作用域,增强了代码的可读性和安全性。

基本语法:

if 语句:

if (初始化语句; 条件判断语句) {
    // ...
} else {
    // ...
}

switch 语句:

switch (初始化语句; 条件判断语句) {
    case label1:
        // ...
        break;
    case label2:
        // ...
        break;
    // ...
}

4. 内联变量(inline variables)

基本概念

在 C++17 之前,静态成员变量和全局变量的声明和定义必须分开:

// MyClass.h
class MyClass {
public:
    static const int value; // 声明
};

// MyClass.cpp
#include "MyClass.h"
const int MyClass::value = 42; // 定义

如果直接在头文件中定义变量,会导致链接错误,因为每个包含头文件的翻译单元都会有该变量的定义。

内联变量的引入

内联变量允许你在头文件中直接定义变量,并保证它在整个程序中只有一个定义。使用 inline 关键字可以实现这一点:

// MyClass.h
class MyClass {
public:
    inline static const int value = 42; // 声明并定义
};

5. std::variant

C++17增加std::variant实现类似union的功能,但却比union更高级,举个例子union里面不能有string这种类型,但std::variant却可以,还可以支持更多复杂类型,如map等

int main() { // c++17可编译
    std::variant<int, std::string> var("hello");
    cout << var.index() << endl;
    var = 123;
    cout << var.index() << endl;

    try {
        var = "world";
        std::string str = std::get<std::string>(var); // 通过类型获取值
        var = 3;
        int i = std::get<0>(var); // 通过index获取对应值
        cout << str << endl;
        cout << i << endl;
    } catch(...) {
        // xxx;
    }
    return 0;
}

注意:一般情况下variant的第一个类型一般要有对应的构造函数,否则编译失败:

struct A {
    A(int i){}  
};
int main() {
    std::variant<A, int> var; // 编译失败
}

为了避免上述情况可以使用std::monostate来打个桩,模拟一个空状态。

std::variant<std::monostate, A> var; // 可以编译成功

6. std::optional

std::optional 是 C++17 中引入的标准库类型,用于表示可能包含值的容器。它的主要目的是解决以下两个问题:

  • 表示可能为空的值: 在某些情况下,一个变量可能没有有效值,传统做法可能是使用特殊的值(如 -1 表示无效),但这种方法不够安全和直观。std::optional 提供了一种类型安全的方式来表示值的存在与否。

  • 避免空指针问题: 在 C++ 中,裸指针或引用可能为空,如果不做有效检查,会导致程序崩溃或未定义行为。std::optional 可以避免这类问题,因为它明确表示是否有值,并提供了安全的访问方式。

主要特性和用法

  • 可选值的声明和初始化: 可以使用 std::optional 来声明一个可以存储类型 T 的可选值对象。
std::optional<int> maybeInt; // 声明一个可能包含 int 类型值的 optional 对象
std::optional<std::string> maybeStr = "Hello"; // 初始化一个包含字符串的 optional 对象
  • 安全的值访问和检查: 可以使用 std::optional 的成员函数 value() 来获取存储的值,或者通过 operator*operator-> 来访问值。在使用前可以通过 has_value() 方法检查是否有值。
std::optional<int> maybeInt = 42;

if (maybeInt.has_value()) {
    std::cout << "Value is: " << maybeInt.value() << std::endl;
} else {
    std::cout << "No value." << std::endl;
}
  • 空状态管理: 如果 std::optional 没有存储值,则视为其处于空状态。可以使用 reset() 方法清除存储的值,或者通过析构函数自动释放资源。
std::optional<double> maybeDouble;

if (!maybeDouble.has_value()) {
    maybeDouble = 3.14; // 设置一个值
}

maybeDouble.reset(); // 清除存储的值

7. std::any

C++17引入了any可以存储任何类型的单个值

int main() { // c++17可编译
    std::any a = 1;
    cout << a.type().name() << " " << std::any_cast<int>(a) << endl;
    a = 2.2f;
    cout << a.type().name() << " " << std::any_cast<float>(a) << endl;
    if (a.has_value()) {
        cout << a.type().name();
    }
    a.reset();
    if (a.has_value()) {
        cout << a.type().name();
    }
    a = std::string("a");
    cout << a.type().name() << " " << std::any_cast<std::string>(a) << endl;
    return 0;
}

8. std::apply

使用std::apply可以将tuple展开作为函数的参数传入

int add(int first, int second) { return first + second; }

auto add_lambda = [](auto first, auto second) { return first + second; };

int main() {
    std::cout << std::apply(add, std::pair(1, 2)) << '\n';
    std::cout << add(std::pair(1, 2)) << "\n"; // error
    std::cout << std::apply(add_lambda, std::tuple(2.0f, 3.0f)) << '\n';
}
#面经##八股##C++#
全部评论

相关推荐

09-21 21:14
门头沟学院
否极泰来来来来:和他说:这里不好骂你,我们加个微信聊
点赞 评论 收藏
分享
评论
7
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务