Posts

C++ 标准中值类别的中文翻译


以下内容是《ISO/IEC 14882:2024》[7.2.1] Value category [basic.lval] 部分的中文翻译。标准文件可从此仓库下载。


          expression         
           /     \           
          /       \          
     glvalue      rvalue     
      /    \      /   \      
     /      \    /     \     
 lvalue     xvalue    prvalue
  • glvalue(泛左值)是一个表达式,其求值结果决定了某个对象或函数的身份(identity)。
  • prvalue(纯右值)是一个表达式,其求值用于初始化一个对象、或在特定上下文中计算某个运算符的操作数,或者是一个类型为 cv void 的表达式。
  • xvalue(亡值)是一个 glvalue,它表示一个其资源可以被重用的对象(通常是因为该对象即将结束其生命周期)。
  • lvalue(左值)是一个不是 xvalue 的 glvalue。
  • rvalue(右值)是一个 prvalue 或者 xvalue。

每一条表达式恰好属于本分类体系中以下三种基本类别之一:lvalue、xvalue 或 prvalue。这种表达式的属性被称为它的值类别(value category)。

[...]

核心 Linux Socket API 用法释义


1. socket()

int socket(int 协议族, int 套接字类型, int 协议);

📖 解释:创建一个新的套接字,返回一个文件描述符用于后续网络通信.

第 1 个参数 🔑:协议族,指定通信协议类型.

  • 示例:AF_INET(IPv4协议)、AF_INET6(IPv6协议)、AF_UNIX(本地进程间通信).

第 2 个参数 🔑:套接字类型,指定通信语义.

  • 示例:SOCK_STREAM(TCP流式套接字)、SOCK_DGRAM(UDP数据报套接字).

第 3 个参数 🔑:具体协议,通常为0(使用默认协议).

  • 示例:若类型是SOCK_STREAM,则默认协议是IPPROTO_TCP;若类型是SOCK_DGRAM,默认协议是IPPROTO_UDP.

返回值 📤

  • ✅ 成功时:返回非负整数(套接字文件描述符).

  • ❎ 失败时:返回-1,并设置errno(如EACCES权限不足、EINVAL参数无效).

[...]

C++变参模板实战:递归继承实现简易元组类


详细的讲解请看 CppMore 里缪大佬的 这篇文章

此处学习一下其中的使用 递归继承 技巧实现简易元组类这个例子。

直接 “Show me the code!”

#include <iostream>

/*
    先明晰省略运算符的意思:
       typename... T   --> 把一堆类型折叠到 T 中
       T ...           --> 从 T 中展开之前折叠的变量
*/

// #1: 先是 Tuple 类

// 主模板
template <typename... Types>
class Tuple;
// 全特化:作为终止递归的条件
template <>
class Tuple<> {};

template <typename Head, typename... Tail>  // 将传递进来的参数分为第 1 个(称之为 Head)和其余个(第 2~N 个,折叠在 Tail 里)
class Tuple<Head, Tail...> : public Tuple<Tail...> { // 递归继承(采用公有继承)
public:
  Tuple() {}

  Tuple(Head v, Tail... vtails) : Tuple<Tail...>(vtails...), head_(v) {}

  // 用来返回 Tuple 类的内部成员 head_ 的值,也是这个 Tuple 的第一个值
  Head &head() { return head_; }

protected:
  // head_ : 第一个值的意思,比如作为 Tuple<int, float, char> t(5, 2.7, 'b') 其中的 5
  Head head_;
};


// #2: 然后是 TupleAt 类

// 主模板
template <std::size_t I, typename... TList>
struct TupleAt;


// 暂称 Tuple<T, TList...> 叫做“当前元组”
// 设计 TupleAt 的目的是获取到指定索引(I)处的元素的类型(称之为ValueType)(不必获取到具体的值,这个任务交给 Tuple 类的 head() 方法解决)
// ValueType 是“尾随元组”(意思是当前元组把第一项元素去掉,剩余的元素构成的元组)的第一个元素的类型
// TupleType 是“尾随元组”这一整体类型(它是当前元组的父类,因为 Tuple<Head, Tail...> 公有继承了 Tuple<Tail...>)
template <std::size_t I, typename T, typename... TList>
struct TupleAt<I, Tuple<T, TList...>> {
  using ValueType = typename TupleAt<I - 1, Tuple<TList...>>::ValueType;
  using TupleType = typename TupleAt<I - 1, Tuple<TList...>>::TupleType;
};


// 作为终止递归的条件:I = 0
template <typename T, typename... TList>
struct TupleAt<0, Tuple<T, TList...>> {
  using ValueType = T;
  using TupleType = Tuple<T, TList...>;
};


// #3: 最后还剩个 TupleGet 函数模板

/* TupleGet<I>(tuple) 这个函数模板在使用的时候(比如 TupleGet<2>(t))直观上接受两个参数:
                    其一,是模板参数 std::size_t I,表示索引;
                    其二,是函数参数 Tuple<TList...>& tuple,表示目标元组
    TList 是目标元组 tuple 的模板参数们
*/
template <std::size_t I, typename... TList>
typename TupleAt<I, Tuple<TList...>>::ValueType &
TupleGet(Tuple<TList...> &tuple) {
  using BaseTupleType = typename TupleAt<I, Tuple<TList...>>::TupleType;
  return static_cast<BaseTupleType &>(tuple).head(); // 把当前元组强制转换其类型为上一层元组(也就是当前元组的尾随元组),.head() 的结果就会变成尾随元组的 head_
}


int
main() {
  Tuple<int, float, char> t(1, 2.7, 'b');
  Tuple<int, Tuple<int, float, char>, double> t2(4, t, 4.7);
  std::cout << TupleGet<2>(t) << std::endl;
  std::cout << TupleGet<0>(t2) << std::endl;
  std::cout << TupleGet<2>(t2) << std::endl;
  std::cout << TupleGet<2>(TupleGet<1>(t2)) << std::endl; // 嵌套元组也能正常处理哦
  return 0;
}

这个程序实现了一个极简元组类,可以把不同类型的元素绑定在一起,并按下表索引访问元素。

[...]

在 Arch Linux 上编译 bloomberg/clang-p2996 小记


本文简要记录笔者在 Arch Linux 上编译一个部分支持 C++ 反射的 clang 编译器的过程。

期待 Reflection 能正式被编译器实现的那一天。 :)

本次实验受群友 Yiran Wang 启发。

克隆仓库

GitHub 仓库在 这里,有关这个编译器的介绍详情请看仓库(默认是 purpose 分支,代码在 p2996 分支上)。

先克隆仓库到本地:

$ git clone https://github.com/bloomberg/clang-p2996 --depth 1 --branch=p2996

使用 --depth 1 选项只克隆最新版本的 commit,可大大减小仓库体积(好像只有两百多兆)。

[...]

C++ <chrono> 库中的高效闰年判断算法解析


在 C++ <chrono> 库中,有如下判断闰年的算法:

class year
{
private:
	short _M_y;

public:
	constexpr bool
	is_leap() const noexcept
	{
		return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0;
	}
};

根据源代码的注释,总结一下优化思路:

判断闰年的规则是:

  • 如果年份能被 4 整除且不能被 100 整除,则是闰年.
  • 如果年份能被 100 整除,则还必须能被 400 整除,才是闰年.

先判断能否被 100 整除比其他思路更快,因为它减少了不必要的计算. 参考这里.

[...]

如何为 gcc 贡献中文翻译


在本地试验

先在本地试一试修改 gcc 的翻译吧. 新建一个用来折腾的文件夹 ~/test/.

$ mkdir ~/test
$ cd ~/test

下载最新翻译

下载 Translation Project 上的最新版 gcc 简体中文翻译文件. 截至发稿时,gcc 最新版本为 14.2.0 版.

$ wget https://translationproject.org/PO-files/zh_CN/gcc-14.2.0.zh_CN.po

下载了一个叫做 gcc-14.2.0.zh_CN.po 的文件.

[...]