当前位置: 首页 > article >正文

C++ STL CookBook

目录

std::optional

std::any

处理我们的时间


std::optional

下面来介绍一下我们的std::optional这个类,std::optional 是 C++17 引入的一个标准库类型,它是一个轻量级的容器,能够表示一个可能没有值的对象。std::optional 用于表示某个值可能存在也可能不存在的情况,它允许我们避免使用特殊的值(如 nullptr-1)来表示缺失的值,提供了更安全、清晰的代码结构。

struct factor_t {
    bool is_prime;
    long factor;
};
​
factor_t factor(long n) {
    factor_t r{};
    for(long i = 2; i <= n / 2; ++i) {
        if (n % i == 0) {
            r.is_prime = false;
            r.factor = i;
            return r;
        }
    }
    r.is_prime = true;
    return r;
}

比如说,我们想要生成一个有特殊含义的值,为了表达它是否有效,一个经典的做法要么是返回一个非法值(类似于-1),要么就是返回一个结构体存储其是否有效(但是受困于其信息存在潜在的不同步性)

这个玩意就是解决这类问题的:

  1. 函数返回值:当函数可能无法返回有效值时,std::optional 是一个非常合适的返回类型。它避免了使用指针(例如 nullptr)或错误代码来表示失败状态。

  2. 存储可选值:在数据结构中,某些字段可能是可选的,使用 optional 可以简洁明了地表示这种情况。

  3. 避免使用特殊标记值:例如,用 -1NULL 表示一个缺失的整数值时,可能会与有效值发生冲突。std::optional 可以避免这种情况。

std::optional 可以通过两种方式创建:

  • 通过值初始化:std::optional<int> opt(10);

  • 通过无参构造:std::optional<int> opt;(表示“空”的 optional

#include <iostream>
#include <optional>
​
int main() {
    std::optional<int> opt1(42);  // 包含值 42
    std::optional<int> opt2;       // 不包含值,默认构造为空
​
    if (opt1) {
        std::cout << "opt1 has value: " << *opt1 << std::endl;
    }
​
    if (!opt2) {
        std::cout << "opt2 does not have value" << std::endl;
    }
​
    return 0;
}

输出:

opt1 has value: 42
opt2 does not have value

可以通过 has_value()operator bool() 来检查 optional 是否包含值。如果 optional 包含值,has_value() 返回 true,否则返回 falseoperator bool() 返回 true 表示包含值,false 表示没有值。

#include <iostream>
#include <optional>
​
int main() {
    std::optional<int> opt1(42);
    std::optional<int> opt2;
​
    if (opt1.has_value()) {
        std::cout << "opt1 has value: " << *opt1 << std::endl;
    }
​
    if (!opt2) {
        std::cout << "opt2 does not have value" << std::endl;
    }
​
    return 0;
}

访问 optional 中的值可以通过解引用(*)或 value() 成员函数。需要注意的是,如果 optional 中没有值,直接解引用会导致未定义行为,因此应先检查值是否存在。

#include <iostream>
#include <optional>
​
int main() {
    std::optional<int> opt1(42);
    std::optional<int> opt2;
​
    if (opt1) {
        std::cout << "opt1 contains: " << *opt1 << std::endl;  // 解引用
    }
​
    if (!opt2) {
        std::cout << "opt2 is empty" << std::endl;
    }
​
    return 0;
}

std::optional 提供了 value_or() 函数,可以为缺失的值提供一个默认值。

#include <iostream>
#include <optional>
​
int main() {
    std::optional<int> opt1(42);
    std::optional<int> opt2;
​
    std::cout << "opt1 value: " << opt1.value_or(0) << std::endl;  // 42
    std::cout << "opt2 value: " << opt2.value_or(0) << std::endl;  // 0
​
    return 0;
}

输出:

opt1 value: 42
opt2 value: 0

std::optional 常用于表示函数的返回值,尤其是在函数可能无法成功返回时。例如,查找操作可能找不到目标值,此时可以返回一个空的 optional

#include <iostream>
#include <optional>
#include <vector>
​
std::optional<int> find_index(const std::vector<int>& vec, int target) {
    for (int i = 0; i < vec.size(); ++i) {
        if (vec[i] == target) {
            return i;  // 找到目标,返回索引
        }
    }
    return std::nullopt;  // 未找到目标,返回空的 optional
}
​
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
​
    auto index = find_index(vec, 3);
    if (index) {
        std::cout << "Found 3 at index: " << *index << std::endl;
    } else {
        std::cout << "3 not found!" << std::endl;
    }
​
    auto missing = find_index(vec, 10);
    if (!missing) {
        std::cout << "10 not found!" << std::endl;
    }
​
    return 0;
}

输出:

Found 3 at index: 2
10 not found!

std::any

刚刚我们擦除的是类型的值,现在我们关心一下,可不可以存储任意类型呢?答案是可以的,std::any 是 C++17 引入的一个标准库类型,用于存储任何类型的单一值。它是一个类型安全的容器,允许在运行时动态地存储任意类型的数据,并且能够在需要时访问这些数据。与 std::variantstd::optional 不同,std::any 允许存储任何类型,而无需在编译时确定类型。

std::any 是一个类模板,用于存储任何类型的对象。在存储值时,std::any 会自动进行类型擦除,即它会存储值的类型信息,并使得我们能够稍后重新获取该值。std::any 提供了一些常用的操作,包括:

  1. 存储值:将一个值存储到 std::any 容器中。

  2. 访问值:可以通过 std::any_cast 来访问存储的值。

  3. 检查类型std::any 提供了 has_value() 方法,可以检查是否存储了值。

std::any 主要用于以下场景:

  1. 存储不同类型的数据:当我们需要存储多种不同类型的数据时,std::any 提供了一个统一的容器,可以不关心具体类型。

  2. 类型擦除:在某些情况下,我们需要存储不同类型的对象,而又不希望每种类型都有自己的容器,这时可以使用 std::any

  3. 与反射相关的操作std::any 提供了一种方式,可以动态地操作不同类型的值,类似于反射。

std::any 可以通过构造函数来初始化并存储任何类型的值。可以直接存储类型,也可以通过 std::make_any 工具函数来构造 std::any 对象。

#include <iostream>
#include <any>
​
int main() {
    std::any a1 = 42;               // 存储一个 int 值
    std::any a2 = std::string("hello");  // 存储一个 string 值
​
    std::cout << "Stored an int and a string." << std::endl;
​
    return 0;
}

std::any 提供了 has_value() 方法来检查是否存储了一个有效的值。如果 std::any 存储了一个值,has_value() 返回 true,否则返回 false

#include <iostream>
#include <any>
​
int main() {
    std::any a1;  // 未初始化的 any 对象
​
    if (!a1.has_value()) {
        std::cout << "a1 does not contain a value." << std::endl;
    }
​
    a1 = 42;  // 给 any 赋一个 int 值
​
    if (a1.has_value()) {
        std::cout << "a1 contains a value." << std::endl;
    }
​
    return 0;
}

访问 std::any 中存储的值需要使用 std::any_cast。如果类型匹配,std::any_cast 会返回对应类型的引用或指针;如果类型不匹配,std::any_cast 会抛出 std::bad_any_cast 异常。

#include <iostream>
#include <any>
#include <string>
#include <exception>
​
int main() {
    std::any a = 42;  // 存储一个 int 类型的值
​
    try {
        int val = std::any_cast<int>(a);  // 正确:类型匹配
        std::cout << "Stored value: " << val << std::endl;
        
        // 错误:类型不匹配
        std::string str = std::any_cast<std::string>(a);  
    } catch (const std::bad_any_cast& e) {
        std::cout << "Failed to cast: " << e.what() << std::endl;
    }
​
    return 0;
}

输出:

Stored value: 42
Failed to cast: bad any cast

std::any 允许在同一个容器中存储不同类型的值。这使得它非常适合存储不确定类型的数据。例如,在某些情况下,程序可能需要处理不同类型的数据并统一存储。

#include <iostream>
#include <any>
#include <string>
​
void print_value(const std::any& a) {
    try {
        if (a.type() == typeid(int)) {
            std::cout << "Integer: " << std::any_cast<int>(a) << std::endl;
        } else if (a.type() == typeid(std::string)) {
            std::cout << "String: " << std::any_cast<std::string>(a) << std::endl;
        } else {
            std::cout << "Unknown type" << std::endl;
        }
    } catch (const std::bad_any_cast& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
}
​
int main() {
    std::any a1 = 42;               // 存储 int
    std::any a2 = std::string("hello");  // 存储 string
​
    print_value(a1);  // 输出:Integer: 42
    print_value(a2);  // 输出:String: hello
​
    return 0;
}

std::anystd::variant 都用于存储多种类型的数据,但它们的使用场景有所不同:

  • std::any 是完全类型擦除的,它允许存储任何类型的对象,无需在编译时指定类型。但是,它的访问需要使用 std::any_cast,并且在运行时进行类型检查。

  • std::variant 是一个类型安全的联合体,它必须在编译时指定所有可能的类型。访问时通过 std::getstd::visit 来安全地获取存储的值,std::variant 在访问时提供了编译时类型检查。

#include <iostream>
#include <variant>
#include <string>
​
using VariantType = std::variant<int, std::string>;
​
int main() {
    VariantType v1 = 42;
    VariantType v2 = std::string("hello");
​
    std::cout << "v1 contains: " << std::get<int>(v1) << std::endl;
    std::cout << "v2 contains: " << std::get<std::string>(v2) << std::endl;
​
    return 0;
}

std::any 相比,std::variant 的类型是固定的,可以在编译时确保类型安全,而 std::any 更灵活,但也需要在运行时进行类型检查。

std::any 不仅可以存储基本数据类型,还可以存储自定义类型。为了访问存储的自定义类型,我们同样使用 std::any_cast,并确保类型匹配。

#include <iostream>
#include <any>
​
class MyClass {
public:
    void hello() { std::cout << "Hello from MyClass!" << std::endl; }
};
​
int main() {
    MyClass obj;
    std::any a = obj;  // 存储自定义类型
​
    try {
        MyClass& ref = std::any_cast<MyClass&>(a);  // 访问存储的 MyClass 对象
        ref.hello();  // 输出:Hello from MyClass!
    } catch (const std::bad_any_cast& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }
​
    return 0;
}

处理我们的时间

std::chrono 是 C++11 引入的一个用于处理时间和时间间隔的库,它提供了一些类和工具来处理时间点、时间段、时钟等。下面是 std::chrono 中一些常见的类及其使用方法:

时钟类用于获取当前时间点。常见的时钟有:

  • std::chrono::system_clock

    • 这是与系统时间相关的时钟。它通常用于获取当前的时间戳。

    • 它的时间表示的是从某个特定的纪元(通常是 1970 年 1 月 1 日 UTC)到当前的时间点。

    示例:

    #include <iostream>
    #include <chrono>
    ​
    int main() {
        auto now = std::chrono::system_clock::now();
        auto duration = now.time_since_epoch(); // 获取自纪元以来的时间
        auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
        std::cout << "Seconds since epoch: " << seconds.count() << std::endl;
        return 0;
    }
  • std::chrono::steady_clock

    • 该时钟与系统时间无关,保证其每次获取的时间点之间的间隔是恒定的(即没有跳跃)。

    • 适用于测量程序运行时间和间隔。

    示例:

    #include <iostream>
    #include <chrono>
    ​
    int main() {
        auto start = std::chrono::steady_clock::now();
        // 执行一些操作
        auto end = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
        std::cout << "Elapsed time: " << duration.count() << " milliseconds" << std::endl;
        return 0;
    }
  • std::chrono::high_resolution_clock

    • 这是精度最高的时钟,通常作为 steady_clock 的实现。它的分辨率取决于系统。

时间点表示某一特定的时刻,通常由时钟与其起始点结合定义。时间点通过 std::chrono::time_point 表示。

  • 获取时间点:

    auto now = std::chrono::system_clock::now();
  • 转换为其他单位:

    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());

std::chrono::duration 表示时间间隔,是对两个时间点之差的描述。它通常通过一个整数和一个单位(如秒、毫秒、微秒等)表示。

常见的时间间隔类:

  • std::chrono::duration<int>:表示整数类型的时间间隔。

  • std::chrono::duration<float>:表示浮动类型的时间间隔。

常见的时间单位:

  • std::chrono::seconds:表示秒。

  • std::chrono::milliseconds:表示毫秒。

  • std::chrono::microseconds:表示微秒。

  • std::chrono::nanoseconds:表示纳秒。

示例:

#include <iostream>
#include <chrono>
​
int main() {
    std::chrono::duration<int> d1(5);  // 5 秒
    std::chrono::duration<double> d2(3.14);  // 3.14 秒
​
    std::cout << "Duration 1: " << d1.count() << " seconds" << std::endl;
    std::cout << "Duration 2: " << d2.count() << " seconds" << std::endl;
​
    // 转换时间单位
    auto d1_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(d1);
    std::cout << "Duration 1 in milliseconds: " << d1_in_ms.count() << " ms" << std::endl;
    
    return 0;
}

std::chrono::duration_cast 用于在不同的时间单位之间进行转换。注意,转换时会丢失一些精度。

示例:

#include <iostream>
#include <chrono>
​
int main() {
    std::chrono::milliseconds ms(1500);  // 1500毫秒
    std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(ms);  // 转换为秒
    std::cout << sec.count() << " seconds" << std::endl;  // 输出1秒
​
    return 0;
}

std::chrono 可以与定时器结合使用,提供程序执行过程中的延时和超时功能。

示例:

#include <iostream>
#include <chrono>
#include <thread>
​
int main() {
    std::cout << "Start waiting..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 程序暂停2秒
    std::cout << "Waited for 2 seconds" << std::endl;
    return 0;
}

你可以定义自己的时间单位,例如 1 毫秒等价于 1000 微秒。

using namespace std::chrono;
using milliseconds = duration<int, std::milli>;  // 定义1毫秒
​
int main() {
    milliseconds ms(500);  // 500毫秒
    std::cout << ms.count() << " milliseconds" << std::endl;  // 输出500
    return 0;
}

http://www.kler.cn/a/449542.html

相关文章:

  • Move AI技术浅析(二):输入与预处理
  • Pandas系列|第二期:Pandas中的数据结构
  • k8s dashboard可视化操作界面的安装
  • docker部署微信小程序自动构建发布和更新
  • 写给Pythoner的前端进阶指南(五):事件驱动模型
  • taiwindcss
  • 拥有人类情感的AI:未来还是幻想?
  • 蓝桥杯刷题——day9
  • AI可信论坛亮点:合合信息分享视觉内容安全技术前沿
  • K8S中的PV、PVC介绍和使用
  • 探秘 DNS 服务器:揭开域名解析的神秘面纱
  • 【已解决】【大数据综合案例】上| Hive与MongoDB配置
  • 【CSS in Depth 2 精译_086】14.3:CSS 剪切路径(clip-path)的用法
  • 探索人工智能及机器学习如何赋能IP代理
  • HTML5 Web IndexedDB 数据库
  • 【chkdsk】chkdsk 按下停止键的后果
  • WPF+MVVM案例实战与特效(四十五)- 打造优雅交互:ListBox 的高级定制与行为触发(侧边菜单交互面板)
  • 直流电机驱动电路分享(HIP4082)
  • 《开启微服务之旅:Spring Boot Web开发举例》(二)
  • C++ 中的字面量类型定义
  • Redis数据对象
  • 谷歌发布首个 AI 推理模型欲挑战 OpenAI o1,AI 领域将展开新的竞争
  • 砂轮磨料基础知识及发展学习笔记
  • k8s-metrics-server
  • 鸿蒙项目云捐助第二十三讲云捐助项目云首页导航功能的实现
  • JavaScript 、ECMAScript、 ECMA-262、TC39??