C++新特性学些笔记

【※】decltype

推导出表达式类型

    int i = 4;
    decltype(i) a; //推导结果为int。a的类型为int。

【※】std::async

        std::async 创建一个异步任务,其不一定会创建一个新线程去执行该任务;

        使用 std::launch::deferred 时,异步任务不会创建一个新线程;

        使用 std::launch::async 时,操作系统会强制创建一个新线程来执行异步任务;

        当 std::async 创建一个异步任务不显式指出使用 std::launch::deferred 或std::launch::async 时, 操作系统会随机采取一种方式来执行异步任务(当系统资源充足时,会优先创建一个新线程,否则就会延迟执行异步任务),可以使用 std::future_status 来判断异步任务处于哪种状态(等待0秒后判断);

【※】std::invoke

std::invoke 是 C++17标准库中引入的一个函数模板,它的引入就是为了解决这个问题,它提供了一种统一的调用语法,无论是调用普通函数、函数指针、类成员函数指针、仿函数、std::function、类成员还是lambda表达式,都可以使用相同的方式进行调用。

 std::invoke 的语法如下:

template <typename Fn, typename... Args>
decltype(auto) invoke(Fn&& fn, Args&&... args);

【※】std::future

std::thread对象很常见,它是C++11中提供异步创建多线程的工具。但是我们想要从线程中返回异步任务结果,一般需要依靠全局变量;从安全角度看,有些不妥;为此C++11提供了std::future类模板,future对象提供访问异步操作结果的机制,很轻松解决从异步任务中返回结果。

在 C++11 及更高版本中,std::future 是一个用于处理异步操作结果的类模板,它提供了一种从异步任务中获取结果的机制。以下是其核心用法和示例:


核心概念

  1. 异步操作句柄:表示一个可能尚未完成的异步操作结果

  2. 阻塞等待:通过 get() 阻塞当前线程直到结果就绪

  3. 一次性读取:结果只能被获取一次(调用 get() 后 future 失效)

  4. 异常传递:异步任务中的异常会在调用 get() 时重新抛出


基本用法示例

1. 配合 std::async 使用(最简单方式)
#include <iostream>
#include <future>
#include <thread>

int compute() {
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 模拟耗时操作
    return 42;  // 返回计算结果
}

int main() {
    // 启动异步任务
    std::future<int> fut = std::async(std::launch::async, compute);

    // 执行其他任务...
    std::cout << "Main thread working...\n";

    // 阻塞获取结果(若未完成则等待)
    int result = fut.get();  
    std::cout << "Result: " << result;  // 输出: Result: 42
}
2. 配合 std::packaged_task 使用
#include <future>
#include <thread>
#include <iostream>

int main() {
    // 包装可调用对象
    std::packaged_task<int()> task([]{
        return 7 * 6;  // 实际计算
    });

    // 获取关联的 future
    std::future<int> fut = task.get_future();

    // 在独立线程中执行任务
    std::thread t(std::move(task));
    t.detach();  // 或 t.join()

    std::cout << "Waiting...\n";
    std::cout << "Result: " << fut.get();  // 输出: Result: 42
}
3. 配合 std::promise 手动设置结果
#include <future>
#include <thread>
#include <iostream>

void worker(std::promise<int> prom) {
    try {
        int result = 42;  // 计算结果
        prom.set_value(result);  // 设置结果
        // 若需设置异常: prom.set_exception(std::current_exception());
    } catch (...) {
        prom.set_exception(std::current_exception());
    }
}

int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();

    std::thread t(worker, std::move(prom));
    t.detach();

    std::cout << "Result: " << fut.get();  // 输出: Result: 42
}

关键成员函数

方法作用
T get()阻塞获取结果(仅调用一次)
bool valid()检查 future 是否关联有效结果
void wait()阻塞直到结果就绪
wait_for(duration)限时等待(返回 future_status::ready/timeout/deferred
wait_until(timeout)等待到指定时间点

进阶技巧

  1. 处理异常

    try {
        fut.get();
    } catch (const std::exception& e) {
        std::cerr << "Async task failed: " << e.what();
    }
  2. 共享 Future (std::shared_future)

    std::shared_future<int> shared_fut = fut.share();  // 可被多次读取
    auto r1 = shared_fut.get();  // 多线程安全
    auto r2 = shared_fut.get();  // 允许多次调用
  3. 检查状态

    if (fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
        // 结果已就绪
    }

使用场景

  • 并行计算任务

  • 后台数据加载

  • 延迟初始化

  • 超时控制(结合 wait_for


注意事项

  1. get() 只能调用一次,第二次调用会导致 std::future_error 异常

  2. 析构关联了 std::async 的 future 会隐式等待任务完成

  3. 避免在未设置结果的 std::promise 析构时等待(会导致 broken_promise 异常)

  4. 线程管理:std::async 的启动策略(std::launch::async 或 std::launch::deferred

掌握 std::future 能显著提升 C++ 异步编程的安全性和效率,建议结合 std::asyncstd::packaged_task, 和 std::promise 灵活使用。

【※】C++ 函数的小括号之后接一个“->”符号表示什么意思

C++11 引入了一种新的函数语法,即使用后置返回类型(trailing return type)来指定函数的返回类型,特别是在涉及到复杂类型、模板和尾返回类型推断时更为方便。

这种语法使用箭头符号 -> 来指示函数的返回类型,并将返回类型放在函数的参数列表之后。例如:

auto add(int a, int b) -> int {
    return a + b;
}

在上述代码中,函数 add 后置返回类型使用箭头符号 -> 指定为 int,与 return 语句中的返回值类型相匹配。

这种语法对于返回类型依赖于函数参数或其他模板参数的情况很有用,因为在函数参数列表之前指定返回类型可能会变得复杂或困难。

需要注意的是,箭头符号 -> 后面必须紧跟返回类型,它不能用于指定其他信息或表达式。

总结来说,C++ 中函数的小括号之后接一个箭头符号 -> 表示函数的返回类型,并且通常用于使用后置返回类型语法来指定函数的返回类型。

【※】C++异步任务触发函数解析

以下这段代码定义了一个函数模板 fire,它用于异步触发一个可调用对象(函数、lambda等),并返回一个 std::future 来获取异步执行的结果。以下是逐部分解析:

template<typename Fn, typename... Args>
auto fire(T trigger, Fn &&fn, Args &&...args)
    -> std::future<decltype(std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...)>
{
    return fire(trigger, mode::queued, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
  1. 模板参数

    • typename Fn:可调用对象的类型(函数、函数对象等)。

    • typename... Args:可变参数包,表示传递给可调用对象的参数类型。

  2. 函数参数

    • T trigger:触发器对象(类型 T 需在外部定义),可能用于标识事件来源或上下文。

    • Fn &&fn:可调用对象的万能引用(保持值类别,支持移动语义)。

    • Args &&...args:参数的万能引用(完美转发参数包)。

  3. 返回类型(尾置返回类型):

    cpp

    std::future<decltype(std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...))>
    • std::invoke(...)
      使用 std::invoke 调用 fn 并传入完美转发的 args...,返回调用结果的类型。

    • decltype(...)
      推导出调用结果的类型(如 voidint 等)。

    • std::future<...>
      最终返回一个异步结果容器,可通过 .get() 获取值或等待执行完成。

  4. 函数体

    cpp

    return fire(trigger, mode::queued, std::forward<Fn>(fn), std::forward<Args>(args)...);
    • 调用另一个重载的 fire 函数,额外传入 mode::queued(表示任务以队列模式执行)。

    • 完美转发所有参数(fn 和 args...),避免不必要的拷贝。


核心作用

  1. 异步执行
    将可调用对象 fn 和参数 args... 封装为一个任务,放入队列中异步执行(通过 mode::queued 指定)。

  2. 结果获取
    返回 std::future,允许调用者在未来获取结果或等待任务完成。

  3. 完美转发
    使用 std::forward 保持参数的值类别(左值/右值),避免额外拷贝,支持移动语义。


示例场景

假设有一个事件系统,当 trigger 事件发生时,需要异步执行一个任务:

cpp

// 定义一个任务:计算 (a + b)
auto task = [](int a, int b) { return a + b; };

// 触发事件,异步执行任务
std::future<int> fut = fire(Event::Click, task, 2, 3);

// 在需要结果时获取(可能阻塞等待)
int result = fut.get(); // 得到 5

关键设计点

  1. 模式控制
    通过 mode::queued 指定任务调度方式(例如立即执行/加入队列)。

  2. 类型安全
    使用 decltype + std::invoke 自动推导返回类型,无需手动指定。

  3. 通用性
    支持任意可调用对象和参数组合(成员函数、普通函数、lambda等)。

注意:此函数依赖另一个重载的 fire 实现(接受 mode 参数),该函数需实现真正的异步逻辑(如将任务提交到线程池)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值