C++17 一些新特性的简单描述

其实很多17的官方新特性早就被很多非官方的库支持,反复验证完善后被官方收录。

1、std::optional

std::optional<vector<int>> list = {} / std::nullopt / {{}};

不就是表示一个值存在与否是可选的吗

注意下{{}}和nullopt的区别,笔者偶尔遇见过相关bug,毕竟通信行业,信息内容中空列表和列表不存在还是不一样的0.0

使用的时候注意先用has_value判断,当然也可以像下面代码一样直接判断,用的时候.value()或者*解引用 来取值。

复制代码
#include <iostream>
#include <optional>
#include <typeinfo>
#include <vector>

int main()
{
    std::optional<std::vector<int>> list = {};
    std::optional<std::vector<int>> list2 = std::nullopt;
    std::optional<std::vector<int>> list3 = {{}};

    if (list)
    {
        std::cout << "list{} has value: " << typeid(list).name() << std::endl;
    }
    else
    {
        std::cout << "list{} does not have value" << std::endl;
    }

    if (list3)
    {
        std::cout << "list3{{}} has value,"
                  << " list3`s type: " << typeid(list3).name() << " (*list3)'s type: " << typeid(*list3).name()
                  << " (list3.value())'s type: " << typeid(list3.value()).name() << std::endl;
    }
    else
    {
        std::cout << "list3{{}} does not have value" << std::endl;
    }

    if (list == list2)
    {
        std::cout << "{} equal std::nullopt" << std::endl;
    }
    else
    {
        std::cout << "{} not equal std::nullopt" << std::endl;
    }
    if (list3 == list2)
    {
        std::cout << "{{}} equal std::nullopt" << std::endl;
    }
    else
    {
        std::cout << "{{}} not equal std::nullopt" << std::endl;
    }

    return 0;
}
复制代码

 

2、结构化绑定

结构化绑定(Structured Bindings)是 C++17 中引入的一项特性,它允许将元组或其他复合类型的成员绑定到单独的变量中。这样做可以简化对复合类型数据的访问和处理,并提高代码的可读性和可维护性。

通过使用 auto 关键字和[] 将需要绑定的元组或复合类型的成员列表括起来。然后,编译器会根据变量的类型自动将相应的成员绑定到声明的变量中。

复制代码
#include <iostream>
#include <tuple>

int main() {
    // 使用结构化绑定访问元组成员
    std::tuple<int, std::string, double> myTuple{42, "Hello", 3.14};
    auto [x, y, z] = myTuple;
    std::cout << "x: " << x << ", y: " << y << ", z: " << z << std::endl;

    // 使用结构化绑定访问数组元素
    int arr[] = {1, 2, 3, 4, 5};
    auto [a, b, c, d, e] = arr;
    std::cout << "a: " << a << ", b: " << b << ", c: " << c << ", d: " << d << ", e: " << e << std::endl;

    // 使用结构化绑定遍历容器
    std::vector<int> vec{1, 2, 3, 4, 5};
    for (auto [index, value] : enumerate(vec)) {
        std::cout << "Index: " << index << ", Value: " << value << std::endl;
    }

    return 0;
复制代码

 

与for循环结合

复制代码
#include <iostream>
#include <vector>
#include <utility>

int main() {
    // 定义一个成员是 pair 的 vector
    std::vector<std::pair<int, double>> vec{{1, 3.14}, {2, 6.28}, {3, 9.42}};

    // 使用结构化绑定访问 pair 的第二个元素
    for (const auto& [-, value] : vec) {
        std::cout << "Second element of pair: " << value << std::endl;
    }

    return 0;
}
复制代码

 

3、if, switch语句中的初始化器

C++17引入了在if语句中使用初始化器的特性。这意味着可以在if语句的条件中初始化变量,并且作用域仅限于if语句的块中。这样做的主要目的是减少变量的作用域范围,从而增加代码的可读性和可维护性。

if:

复制代码
#include <iostream>

int main() {
    if (int x = 42; x > 0) {
        std::cout << "x is positive" << std::endl;
    } else {
        std::cout << "x is non-positive" << std::endl;
    }

    // 编译时错误:x不在这个作用域内可见
    // std::cout << "x is: " << x << std::endl;

    return 0;
}
复制代码

switch与if类似:

复制代码
#include <iostream>

int main() {
    int option = 2;
    
    switch (int result = option * 2; result) {
        case 1:
            std::cout << "Result is 1" << std::endl;
            break;
        default:
            std::cout << "Result is not 1" << std::endl;
            break;
    }

    // 编译时错误:result 不在此作用域内可见
    // std::cout << "Result is: " << result << std::endl;

    return 0;
}
复制代码

 

4、内联变量

C++17 增强了内联变量(inline variables)特性,允许在头文件中定义全局或静态变量而不会导致多重定义错误。内联变量的声明和定义都需要使用 inline 关键字。

静态常量表达式会被编译器默认添加inline。

复制代码
// header.h
#pragma once

// 1、内联变量在头文件中定义,也就默认所有编译单元中这个变量定义是相同的
inline int globalVariable = 42;

// 2、静态常量表达式变量会被编译器默认添加 inline 修饰
class MyClass {
public:
    // 与 inline static constexpr int MAX_SIZE = 100;等效
    static constexpr int MAX_SIZE = 100;
};
复制代码

5、折叠表达式

  C++17 引入了折叠表达式(Fold Expressions)这一新特性,它允许以更简洁的方式处理模板参数包的展开。折叠表达式提供了一种在编译时展开参数包,并执行一系列操作的方法。

  语法形式:(... op pack) 或 (pack op ...),其中 op 是一个二元操作符,pack 是模板参数包。通过使用不同的操作符和不同的展开方式,可以执行不同的操作,例如求和、求积、连接字符串等。

复制代码
#include <iostream>
#include <vector>
#include <string>

// 求和
template<typename... Args>
auto sum(Args... args) {
    return (args + ...);
}

// 求积
template<typename... Args>
auto product(Args... args) {
    return (args * ...);
}

// 连接字符串
template<typename... Args>
std::string concatenate(Args... args) {
    return (args + ...);
}

// 打印参数包中的元素
template<typename... Args>
void print(Args... args) {
    std::cout << ... << args << std::endl;
}

// 递归展开参数包并打印
template<typename T, typename... Args>
void recursive_print(T t, Args... args) {
    std::cout << t << std::endl;
    if constexpr (sizeof...(args) > 0) {
        recursive_print(args...);
    }
}

int main() {
    // 使用折叠表达式求和、求积、连接字符串
    std::cout << "Sum: " << sum(1, 2, 3, 4, 5) << std::endl;           // Sum: 15
    std::cout << "Product: " << product(1, 2, 3, 4, 5) << std::endl; // Product: 120
    std::cout << "Concatenation: " << concatenate("Hello", ", ", "world", "!") << std::endl; // Concatenation: Hello, world!

    // 使用折叠表达式打印参数包中的元素
    print("Hello", ", ", "world", "!"); // Hello, world!

    // 使用递归展开参数包并打印
    recursive_print("Recursive", "print", "example"); // Recursive, print, example

    return 0;
}
复制代码

 

6、constexpr if

  C++17 引入,它允许在编译时进行条件判断,并根据条件选择不同的代码路径执行。这种语法形式允许在模板和常量表达式中使用条件分支,以便根据编译时的条件来生成不同的代码

if constexpr (condition) {
    // 如果 condition 为真,在编译时执行此代码块
    // 只有在编译时符合条件才会生成这段代码
} else {
    // 如果 condition 为假,在编译时执行此代码块
    // 只有在编译时不符合条件才会生成这段代码
}

  由于 constexpr if 在编译时进行条件判断,所以不满足条件的代码路径在生成的代码中将被消除,这有助于优化生成的可执行代码。也就是说,未进入分支的语法错误是不被关注的哦。

 

 

简单的说明,有时间再深究

部分代码借助AI生成,懒,就是好评0.0

posted @   W-cats  阅读(194)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示