Welcome to My Blog

欢迎光临!文章不定期更新,希望能对您有帮助 :)

我该用什么参数类型?

本文我们讨论一个很小的问题:不同的函数参数类型都在什么情况下使用? 参数分类 比如我们有一个类型 T(让问题先简单点,此处 T 并非范型),那么要在函数中传入一个该类型的参数,共有几种可能?我们可以分一下类: 修饰符:有 const 修饰、无 const 修饰 是否引用:值、左值引用、右值引用 按照这个分类,我们总共能得出 5 种参数类型 const修饰 是否引用 参数类型 有 值 const T 有 左值引用 const T& 无 值 T 无 左值引用 T& 无 右值引用 T&& const T&& 是没有意义的 接下来介绍这些参数类型的应用场景 转移所有权的情况 假设需要将参数的生命周期转移到函数内,那么大多数情况下,直接使用 T 作为参数类型即可。 例如我们有类型 A,其构造时会接受并持有一个 std::vector<int> 参数。那么参数类型应当为 std::vector<int>。 class A { public: A(std::vector<int> v) : v_(std::move(v)) {} private: std::vector_<int> v_; }; 我们考虑两种构造类型 A 对象的情况。其一是我们在传入 std::vector<int> 对象后不会再使用该对象。这时我们可以采取移动语义。 std::vector<int> v; // ... A a(std::move(v)); 其二是我们在传入 std::vector<int> 对象后还会使用该对象。这是我们需要采取复制语义。...

八月 1, 2025 · 2 分钟 · 364 字 · Wokron

试试触发(几乎)所有信号

输入 kill -L,你可以看到 Linux 下所有可用的标准信号,总共有 31 个。 $ kill -L 1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS 8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM 15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN 22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH 29 POLL 30 PWR 31 SYS 这回试试在这些信号原本的应用场景下触发它们。...

五月 23, 2025 · 10 分钟 · 2058 字 · Wokron

Linux 进程的内存管理

虽然我们都学过一个进程的内存由堆和栈组成。但是这样的模型还是太抽象了,其中掩盖了许多操作系统的细节。所以这里简单梳理一下进程的内存管理有关知识。 堆的增长 libc 中提供 malloc 函数申请堆上内存。底层由 brk 系统调用负责申请堆上内存。 在内核的视角下,堆空间是一个简单的结构。它由一个固定的堆底(符号 end)和可变的堆顶(称为 program break)组成。内核所要做的就是根据用户设定的 program break 将堆底和堆顶之间的内存标为有效。而 brk 系统调用的作用便是将某一地址设置为堆顶。 在 brk 系统调用之上,glibc 提供了两个不同的函数 int brk(void *addr) 和 void *sbrk(intptr_t increment)。前者直接设置 program break 地址,后者则根据 increment 取值调整 program break 位置。 下面使用 sbrk() 函数进行内存分配。sbrk() 会返回调用之前原本的 program break 地址,因此还需额外进行加减以获取当前地址。这一语义比较合理,因为这样 brk(N) 和 malloc(N) 的返回值都代表新分配的内存的起始地址。 #include <stdio.h> #include <unistd.h> extern char end; int main() { printf("Address of end symbol: %p\n", (void *)&end); printf("Current program break: %p\n", sbrk(0)); printf("New program break: %p\n", sbrk(1024) + 1024); printf("After deallocation: %p\n", sbrk(-512) - 512); } 运行结果如下...

五月 17, 2025 · 7 分钟 · 1484 字 · Wokron

写个编译期排序

这回摆弄一下模板。 C++ 里有一个 std::integer_sequence。可以定义编译期整数序列。比如说 #include <utility> using my_seq = std::integer_sequence<int, 1, 2, 3, 4, 5>; 接下来写一个 trait seq_sort_t,实现编译期排序。类似于: using my_seq = std::integer_sequence<int, 2, 5, 3, 1, 4>; using sorted_my_seq = seq_sort_t<my_seq>; // std::integer_sequence<int, 1, 2, 3, 4, 5> 首先做一些准备工作。 运行时输出 通过类型萃取,将 std::integer_sequence 转换为运行时的 std::initializer_list。方便输出结果。 template <typename S> struct seq_to_init_list; template <typename T, T... Is> struct seq_to_init_list<std::integer_sequence<T, Is...>> { static constexpr std::initializer_list<T> value = {Is...}; }; template <typename S> constexpr auto seq_to_init_list_v = seq_to_init_list<S>::value; 用法如下:...

三月 7, 2025 · 6 分钟 · 1114 字 · Wokron

Corio - 基于 Asio 的 C++20 协程框架

最近研究了一下协程和网络编程,结合 C++20 的 Coroutine 和 Asio 编写了一个轻量级的协程框架 Corio。Corio 与 Asio 无缝集成,提供了多线程运行时支持和灵活的协程控制接口。 项目仓库:wokron/corio。 安装 corio 是一个 Header-only Library。因此只需要将本仓库 include/ 路径下的一系列头文件放在你的项目中指定位置,并将该位置添加到编译器的包含路径中即可。 以 CMake 为例,为了保持项目的模块化,可以选择将本项目作为 git 子模块。 git submodule add https://github.com/wokron/corio.git ./your/path/to/corio 随后在 CMakeLists.txt 中引入 corio。 add_library(corio INTERFACE) target_include_directories(corio INTERFACE ./your/path/to/corio/include) target_link_libraries(corio INTERFACE ${ASIO_LIBRARY}) 如前所述,Corio 依赖了 Asio。因此为了使用 Corio,还需安装 Asio(非 Boost 版本)。 最后,在你的代码中包含 corio.hpp 头文件以引入 corio。corio 的所有功能均位于 corio:: 命名空间下。 #include <corio.hpp> 使用 协程类型 lazy corio::Lazy<T> 是 corio 库的核心。通过将函数的返回值设定为 Lazy<T>,我们将该函数定义为协程。在 Lazy<T> 所定义的协程中,不再使用 return,而是使用 co_return 以便从协程中返回。其中 T 为该协程的返回值类型。...

一月 21, 2025 · 6 分钟 · 1215 字 · Wokron

编译 Tensorflow 踩坑

前段时间发现了 Tensorflow 里的一处小 Bug,现在有空正好提一个 PR。Bug 很快就修好了,不过之后进行本地编译时我却踩了不少坑。现在记录一下。 一、各种版本傻傻分不清 在开始编译之前,需要介绍一下相关的 Nvidia GPU 依赖项。 Nvidia 有不同架构的各型显卡。为了区分硬件上的区别,Nvidia 使用计算能力(Compute Capability)加以区分。计算能力版本分为两部分 x.y。大版本号表示计算架构(如 Pascal、Volta、Ampere 等等)上的变化,之间不可兼容;小版本号则表示同一架构内部的差别,更高版本可以兼容更低版本。 GPU 驱动(GPU Driver)为操作系统提供硬件驱动。其版本可以通过 nvidia-smi --query-gpu=driver_version --format=csv 找到。同一版本的驱动支持一系列不同计算能力、不同架构的显卡。 CUDA 驱动(CUDA Driver)在 GPU 驱动之上提供了 CUDA 接口。与 GPU 驱动属于内核态设备驱动不同,CUDA 驱动是一用户态的动态链接库(DSO)。CUDA 驱动的版本一般应当随着 GPU 驱动版本的更新而更新。 CUDA Toolkit 提供了构建 CUDA 程序所需的编译器、运行时和库。构建后的 CUDA 应用程序依赖于 CUDA 驱动所提供的接口。又由于 CUDA 是向后兼容(Backward Compatibility)的,所以旧的 CUDA 应用程序可以运行在新的 CUDA 驱动上;换句话说,要运行某一 CUDA 程序,需要高于特定版本的 CUDA 驱动。 向后兼容中的 Backward 指的是与时间上在前的进行兼容。这似乎是中英文导致的思维差异。 二、构建配置 在构建 Tensorflow 时添加 CUDA 支持后,Tensorflow 需要满足 CUDA Toolkit 和 CUDA 驱动之间的兼容性要求。如前所述,为了更强的兼容性,我们希望 CUDA Toolkit 的版本较低。但更高的 Tensorflow 版本又会需要更高版本的 CUDA 特性。因此在编译时需要在这两方面进行权衡。...

一月 20, 2025 · 3 分钟 · 602 字 · Wokron