目录

2023_10_01~10_03 ,阅读了 C++ n4861 草案中的声明符和表达式词法、类型类别

前言

这篇随笔单纯记录 C++20 最终工作草案中非常 dirty work 的 token 部分阅读。不具备知识分享的意义。

参考 C++20 最终草案 n4861。C++ 工作草案本身不完备,我们也不按住相对完备进行定义,而是涉及一些相对常见的内容。

约定:这里大量涉及了文法,我们希望看见诸如

A:
  x
  y
  z

的形式时,可以不严谨但正确地理解作 \(A \Rightarrow x \ \textbf{or} \ y \ \textbf{or} \ z\) 而不是 \(A = \{ x, y, z \}\) ,否则会产生很多误解。

表达式的文法

表达式

expression:
    assignment-expression
    expression , assignment-expression

例:

// 表达式 `expr1` 推出 `assignment-expression` 。
int a = expr1;  // expr1 = x, expr1 = x %= m, ...
// 表达式 `(expr1, expr2)` 推出 `expression , assignment-expression` 。
// 且 `expr2` 应被推出 `assignment-expression` 且无能力提升为 `expression` ,即 `expr1` 极大。
int a = (expr1, expr2);  // expr1 = (x, y), expr2 = z %= m...

常量表达式

constant-expression:
    conditional-expression

注:词法上 constant-expression \(\Rightarrow\) conditional-expression 是满足前提条件并自然成立的。但反过来则需要在语义上检查到一个 conditional-expression 确实是 constant-expression 时才可以提升。

  • 总的来说,如果一个 conditional-expression 在编译期求值,就能提升成 constant-expression ,否则不能。

例:

const int n = 20;
int a = n > 5 : 10 : 1;  // `n > 5 : 10 : 1` 是可被提升为 `constant-expression` 的 `conditional-expression` 。

int m = 20;
int b = m > 5 : 10 : 1;  // `m > 5 : 10 : 1` 是不能提升为 `constant-expression` 的 `conditional-expression` 。

const int h = 20;
int c = h; // `h` 可以被提升成 `conditional-expression` ,但不能进一步提升成 `constant-expression` ,因为 c 不是 constant 的。

赋值表达式

assignment-expression:
    conditional-expression
    logical-or-expression assignment-operator initializer-clause

assignment-operator: one of
    =  *=  /=  %=  +=  -=  >>=  <<=  &=  ^=  |=

例:

// expr 是 `assignment-expression`
int a = expr;  // expr = x |= mask;

// `expr2` 可以是 `expression` 下降得到的 `logical-or-expression` 。
// `mask` 可以是 `initialzer-clause` 下降得到的 `assignment-expression` 。
// `|` 是 `assignment-operator`
int b = expr2 | mask;  // expr2 = x | mask;

初始化器的文法

初始化器

initializer:
    brace-or-equal-initializer
    ( expression-list )

brace-or-equal-initializer:
    = initializer-clause
    braced-init-list

initializer 的例子:

int x{5};  // {5} 是 initializer 也是 brace-or-equal-initializer 。
int x = 5;  // = 5 是 initializer 也是 brace-or-equal-initializer 。
std::vector<int> v(10, 0);  // (10, 0) 是 initializer 也是 ( expression-list ) 。

初始化器子句

initializer-clause:
    assignment-expression
    braced-init-list

initializer-clause 的例子

? = expr;

// expr = x / y ,`x / y` 是一个 `assignment-expression` 。
// expr = expression ,`expr` 是一个可以推出 `assignment-expression` 的 `expression` 。
// expr = {x, y} ,`{x, y}` 是一个 `braced-init-list` 在词法上合法。比如左边是 pair<T, T> a = {x, y} 则语法上也合法。

表达式列表、初始化器列表

expression-list:
    initializer-list

initializer-list:
    initializer-clause
    initializer-list , initializer-clause

注:实际上 (expression-list) 中存在着 expression-list \(\Rightarrow\) initializer-list ,即使是词法上是单分支,但在语法/语义层面也是有必要区分的。而 initiallizer-list 看起来就是 initializer-clause 的左递归。

initializer-list 的例子:

// `1, 2, 3` 是 `initializer-list` 。
int a[] = {1, 2, 3};


// `{1, 2}, {3, 4}` 是 `initializer-list` 。
// 内层的 `1, 2` 和 `3, 4` 也是 `initializer-list` 。
// `{{1, 2}, {3, 4}}` 也是 `initializer-list` ,这里是作为 `initializer-clause` 提升。
// 于是 `{1, 2}, {3, 4}` 也是 `initializer-list , initializer-clause` 。
int m[2][2] = {{1, 2}, {3, 4}};

简单声明的文法

简单声明

simple-declaration:
    decl-specifier-seq init-declarator-list_opt ;
    attribute-specifier-seq decl-specifier-seq init-declarator-list ;
    attribute-specifier-seq_opt decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ;

属性说明符

常见的 attribute-specifier :

  • [[maybe_unused]] ,某个实体可能不被使用。
    • 比如依赖一个表达式的副作用而不是返回值。
  • [[nodiscard]] ,如果某个函数的返回值被丢弃则警告。
    • 比如信号处理中,信号通常需要使用而不能丢弃。

simple-declaration 的例子

// >>> decl-specifier-seq init-declarator-list_opt ; >>>
// decl-specifier-seq: staitc int
// init-declarator-list: x
static int x;

// decl-specifier-seq: int
// init-declarator-list: a = 1, b = 2
int a = 1, b = 2;

// decl-specifier-seq: void
// init-declarator-list: f()
void f();

// decl-specifier-seq: class C
// init-declarator-list: ε
class C;
// 上述代码是一个前置声明。
// 前置声明通常能让编译更快,但会引入不可见的包含依赖,代码错误暴露的位置会让人困惑。
// 很多代码构建工具依赖头文件而不是前置声明。
// 谷歌:除非特定场景下使用前置声明有显著的利大于弊,否则不使用。

// decl-specifier-seq: const char
// init-declarator-list: *p = "hi"
const char* p = "hi";
// <<< decl-specifier-seq init-declarator-list_opt ; <<<

// >>> attribute-specifier-seq decl-specifier-seq init-declarator-list ; >>>
// attribute-specifier-seq: [[nodiscard]]
// decl-specifier-seq: int
// init-declarator-list: f()
[[nodiscard]] int f();

// attribute-specifier-seq: [[maybe_unused]]
// decl-specifier-seq: int
// init-declarator-list: x
[[maybe_unused]] int x;
// <<< attribute-specifier-seq decl-specifier-seq init-declarator-list ; <<<

// >>> attribute-specifier-seq_opt decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ; >>>
// attribute-specifier-seq_opt: 无
// decl-specifier-seq: auto
// ref-qualifier_opt: 无
// [ identifier-list ]: [x, y]
// initializer: = f()
auto [x, y] = f();

// attribute-specifier-seq_opt: [[maybe_unused]]
// decl-specifier-seq: auto
// ref-qualifier_opt: &&
// [ identifier-list ]: [a, b]
// initializer: = getPair()
[[maybe_unused]] auto&& [a, b] = getPair();
// <<< attribute-specifier-seq_opt decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ; <<<

声明说明符的文法

声明说明符序列

decl-specifier-seq:
	decl-specifier attribute-specifier-seq_opt
	decl-specifier decl-specifier-seq

del-specifier-seq 的例子:

// >>> decl-specifier attribute-specifier-seq_opt >>>
// decl-specifier: int , attribute-specifier-seq_opt: ε
int x;

// decl-specifier: int , attribute-specifier-seq_opt: [[maybe_unused]]
int [[maybe_unused]] x;
// <<< decl-specifier attribute-specifier-seq_opt <<<

// >>> decl-specifier decl-specifier-seq >>>
// decl-specifier-seq 在这里右递归,decl-specifier 总是靠左修饰右边的内容

// decl-specifier: static
// decl-specifier-seq: int
// decl-specifier: int
// attribute-specifier-seq_opt: ε
static int x;

// decl-specifier: const
// decl-specifier-seq: int
// decl-specifier: int
// attribute-specifier-seq_opt: ε
const int x;

// decl-specifier-seq -> static + (const + (int + ε))
// decl-specifier: static
// decl-specifier-seq : const int
// decl-specifier : const
// decl-specifier-seq : int
// decl-specifier : int
// decl-specifier-seq : ε
static const int x;

inline static int x;  // decl-specifier-seq -> inline + (static + (int + ε))
constexpr int x = 1;  // decl-specifier-seq -> constexpr + (int + ε)
typedef int T;  // decl-specifier-seq -> typedef + (int + ε)

声明说明符

decl-specifier:
	storage-class-specifier
	defining-type-specifier
	function-specifier
	friend
	typedef
	constexpr
	consteval
	constinit
	inline

存储类说明符

storage-class-specifier:
    static
    thread_local
    extern
    mutable

storage-class-specifier 的例子:

// 文件内的辅助函数
// 内部链接,只在本翻译单元可见
static void helper() { }

// 线程局部的缓存
// 每个线程各有一份独立的 cache
thread_local std::vector<int> cache;

// 头文件里声明的全局变量
// 声明 g_count 在别处定义
extern int g_count;

// const 成员函数里可修改的缓存
struct S {
    // 允许在 const 成员函数里修改 cache
    mutable int cache = 0;

    int get() const { return ++cache; }
};

定义类型说明符

defining-type-specifier:
    type-specifier
    class-specifier
    enum-specifier

defining-type-specifier 的例子:

int x;
// int 是 defining-type-specifier 也是 type-specifier 。

class C { };
// class C { } 是 defining-type-specifier 也是 class-specifier 。

enum class E { OK, FAIL };
// enum class E { OK, FAIL } 是 defining-type-specifier 也是 enum-specifier 。
类型说明符
type-specifier:
	simple-type-specifier
	elaborated-type-specifier
	typename-specifier
	cv-qualifier
简单类型说明符
simple-type-specifier:
	nested-name-specifier_opt type-name
	nested-name-specifier template simple-template-id
	decltype-specifier
	placeholder-type-specifier
	nested-name-specifier_opt template-name
	char
	char8_­t
	char16_­t
	char32_­t
	wchar_­t
	bool
	short
	int
	long
	signed
	unsigned
	float
	double
	void

简单类型说明符基本都比较常见,下面举一些例子。

simple-type-specifier 的例子:

// >>> nested-name-specifier_opt type-name >>>
MyClass obj;
// `nested-name-specifier` : ε ,`type-name` : `MyClass` .

std::string s;
// `nested-name-specifier` : `std::` ,`type-name` : `string` .
std::vector<int> v;
// `nested-name-specifier` : `std::` ,`type-name` : `vector<int>` .

// nested-name-specifier : A::B::,type-name : C
A::B::C c;

using T = int;  // alias-declaration
T x;  // `nested-name-specifier` : ε , type-name` : `T` .

enum E { a };  // decl-specifier
E e;  // `nested-name-specifier` : ε , `type-name` : `E` .
// <<< nested-name-specifier_opt type-name <<<

// >>> nested-name-specifier template simple-template-id >>>
template<typename T>
void f() {
  typename T::template rebind<int> x;
  // `simple-type-specifier` : `T::template rebind<int>` .
  // `nested-name-specifier` : `T::` .
  // `template` : `template` .
  // `simple-template-id` : `rebind<int>` .
}
// <<< nested-name-specifier template simple-template-id <<<

// >>> decltype-specifier >>>
int a = 1, b = 2;
decltype(a + b) c;
// `decltype-specifier` : `decltype(a + b)` .
// <<< decltype-specifier <<<

// >>> placeholder-type-specifier >>>
auto x = 42;
decltype(auto) y = x;
// `placeholder-type-specifier` : `auto`
// <<< placeholder-type-specifier <<<

// >>> nested-name-specifier_opt template-name >>>
template<class T>
struct A {
  A* p;
  // `simple-type-specifier` : `A` .
  // `nested-name-specifier_opt` : ε .
  // `template-name` : `A` .
};

namespace N {

template<class T>
struct B {
  N::B* q;
  // `simple-type-specifier` : `N::B` .
  // `nested-name-specifier_opt` : `N::`
  // `template-name` : `B`
};

}  // namespace N
// <<< nested-name-specifier_opt template-name <<<

这里主要说的是字符类型。

标准保证 char 是字节大小的,但不保证底层实现,现在编译器一般是 unisgned int 或 int 实现的。

wchar_t 是长字符,标准要求它的长度至少能够支持本地字符集,相关函数很完善, Windows 原生 API 大量使用了 wchar_t 。

印象中谷歌 C++ 风格指南中说过,它在非 Windows 交互环境中是不被推荐使用的,如果需要使用长字符,应尽量使用 charN_t 这种定长字符。因为源代码的默认编码方式通常是 UTF-8 ,默认情况下应该使用的定长字符是 char8_t 。

详细类型说明符

elaborated-type-specifier 的例子:

// 前置声明。
class Widget;
// `elaborated-type-specifier` : ``class Widget`` .

struct Point { int x; };
// C 语言场景。
struct Point p;
// `elaborated-type-specifier` : `struct Point` .
// C++ 中允许直接写 `Point` ,此时 `Point` 是 `simple-type-specifier` 中
// 的 `nested-name-specifier_opt type-name` 。
类型名说明符

typename-specifier 的例子:

template <typename Container>
// 只有模板实例创建的时候编译器才知道是什么类型,不能静态推导,所以这里 `typename` 要写。
typename Container::value_type sum(const Container& c) {
// `typename-specifier` : `typename Container::value_type` .
    typename Container::value_type total{}; // 默认调用构造,比如 int x{}; 则 x = 0 。
    for (const auto& x : c) {
        total += x;
    }
    return total;
}
cv 限定符

cv-qualifier 的例子:

const volatile int x = 0;
// `const volatile` 是 `cv-qualifier` .

const int y = 0;
// `const` 是 `cv-qualifier` .

volatile int x = 0;
// `volatile` 是 `cv-qualifier` .
类说明符

class-specifier 的例子:

class Point { int x; int y; };
// `class Point { int x; int y; }` 是 `class-specifier` .

struct S { int a; };
// `struct S { int a; }` 是 `class-specifier` .

union U { int i; float f; };
// `union U { int i; float f; }` 是 `class-specifier` .
枚举说明符

enum-specifier 的例子:

enum class Status : unsigned char { OK, FAIL };
// `enum class Status : unsigned char { OK, FAIL }` 是 `enum-specifier` 。

函数说明符

function-specifier:
    virtual
    explicit

function-specifier 的例子:

struct Shape {
  // 基类里用 虚说明符 `virtual` 声明虚函数,派生类可重写。
  virtual double area() const;

  // 单参数构造函数用 显示说明符 `explicit` ,阻止隐式转换。
  explicit Shape(int);
};

声明符的文法

初始化声明符

初始化声明符列表

init-declarator-list:
	init-declarator
	init-declarator-list , init-declarator

init-declarator-list 的例子:

int x;
// `init-declarator-list` : ε , `init-declarator` : `x` .

int x = 1;
// `init-declarator-list` : ε , `init-declarator` : `x = 1` .

int x, y;
// `init-declarator-list` : `x` , `init-declarator` : `y` .

int x = 1, y = 2;
// `init-declarator-list` : `x = 1` , `init-declarator` : `y = 2` .

int x = 1, *p = &x, &r = x;
// `init-declarator-list` : `x = 1, *p = &x` , `init-declarator` : `&r = x` .

初始化声明符

init-declarator:
	declarator initializer_opt
	declarator requires-clause

init-declarator 的例子:

// >>> declarator initializer_opt >>>
int x;
// `declarator` : `x` , `initializer_opt` : ε .

int y = 1;
// `init-declarator` : `y = 1` , `declarator` : `y` , `initializer_opt` : `= 1` .

int *p = nullptr;
// `declarator` : `*p` , `initializer_opt` : `= nullptr` .
// <<< declarator initializer_opt <<<

// >>> declarator requires-clause >>>
void f() requires true;
// `declarator` : `f()` , `requires-clause` : `requires true` .

template<typename T> void g(T) requires std::integral<T>;
// `declarator` : `g(T)` , `requires-clause` : `requires std::integral<T>` .
// <<< declarator requires-clause <<<

约束子句

requires-clause:
    requires constraint-logical-or-expression

requires-clause 的例子:

// 整型和浮点各提供一个重载,由约束区分:
// f(1)   选中整型版本,因为 std::integral<int> 满足
// f(1.0) 选中浮点版本,因为 std::floating_point<double> 满足
// 约束在重载决议阶段就排除不满足的候选,不会进入函数体;
// 若没有约束,则要实例化到函数体内部才报错
template<typename T>
requires std::integral<T>
void f(T) { }
// `requires-clause` : `requires std::integral<T>: .

template<typename T>
requires std::floating_point<T>
void f(T) { }
// requires std::floating_point<T> 是 requires-clause

声明符

declarator:
    ptr-declarator
    noptr-declarator parameters-and-qualifiers
    noptr-declarator parameters-and-qualifiers trailing-return-type

这里为了避免误会,指针声明符 ptr-declarator 应理解为可能以指针操作符 ptr-operator 开头的声明符。

declatator 的例子:

int *p;
// `ptr-operator` : `*` , `declarator-id` : `p` .

int x;
// `ptr-declarator` : `x` .

int (*fp)(int);
// `noptr-declarator` : `(*fp)` , `parameters-and-qualifiers` : `(int)` .

auto add(int a, int b) -> int {
    return a + b;
}
// `noptr-declarator` : `add` ,
// `parameters-and-qualifiers` : `(int a, int b)` ,
// `trailing-return-type` : `-> int` .

指针声明符

可能以指针操作符开头的声明符。

ptr-declarator:
	noptr-declarator
	ptr-operator ptr-declarator

ptr-declarator 的例子:

int x;
// `noptr-declarator` : `x` .

int *p;
// `ptr-operator` : `*` , `ptr-declarator` : `p` .
// `p` 递归, `noptr-declarator` .

int **pp;
// `ptr-operator` : `*` , `ptr-declarator` : `*pp` .
// `*pp` 递归,`ptr-operator` : `*` , `ptr-declarator` : `pp` .
// `pp` 递归, `noptr-declarator` .

int C::*pm;
// `ptr-operator` : `C::*`,`ptr-declarator` : `p,` .

int &r = x;
// `ptr-operator` : `&` , `ptr-declarator` : `r` .

无指针声明符

noptr-declarator:
	declarator-id attribute-specifier-seq_opt
	noptr-declarator parameters-and-qualifiers
	noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt
	( ptr-declarator )

noptr-declarator 的例子:

// p 是指向 int[10] 的指针。
int (*p)[10];
// `(*p)[10]` is `noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt` .
// `noptr-declarator` : `(*p)` , `constant-expression` : `10` , `attribute-specifier-seq` : ε .
//
// `(*p)` is `( ptr-declarator )` .
// `ptr-declarator` : `*p` .
//
// `*p` is `ptr-operator ptr-declarator` .
// `ptr-operator` : `*` ,`ptr-declarator` : `p` .
//
// `p` is `noptr-declarator` -> `p` is `declarator-id` .

// a 是包含 10 个元素的数组,元素是 int* 。
int* arr[10];
// `*arr[10]` is `ptr-operator ptr-declarator` .
// `ptr-operator` : `*` , `ptr-declarator` : `arr[10]` .
//
// `arr[10]` is `noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt` .
// `noptr-declarator` : `arr` ,
// `constant-expression` : `10` ,
// `attribute-specifier-seq_opt` : ε .
//
// `arr` is `noptr-declarator` -> `arr` is `declarator-id` .

// a 是包含 10 个元素的数组,每个元素是指向 int[20] 的指针。
int (*a[10])[20];
int (*a[10])[20];
// `(*a[10])[20]` is `noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt` .
// `noptr-declarator` is `(*a[10])` , `constant-expression` : `20` ,
//
// `(*a[10])` is `( ptr-declarator )` .
// `ptr-declarator` : `*a[10]` .
//
// `*a[10]` is `ptr-operator ptr-declarator` .
// `ptr-operator` : `*` , `ptr-declarator` : `a[10]` .
//
// `a[10]` is `noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt` .
// `noptr-declarator` : `a` , `constant-expression` : `10` .
//
// `a` is `noptr-declarator` , `a` is `declarator-id` .

// fp 是指向函数的指针,该函数接受 int 并返回 int 。
int (*fp)(int);
// `(*fp)(int)` is `noptr-declarator parameters-and-qualifiers` .
// `noptr-declarator` : `(*fp)` , `parameters-and-qualifiers` : `(int)` .
//
// `(*fp)` is `( ptr-declarator )` .
// `ptr-declarator` : `*fp` .
//
// `*fp` is `ptr-operator ptr-declarator` .
// `ptr-operator` : `*` , `ptr-declarator` : `fp` .
//
// `fp` is `noptr-declarator` -> `fp` is `declarator-id`

// f(int) -> int (*)(douuble) 。这是一个作为公共接口的声明。
// `f` 是一个函数,接受 `int` 。返回一个指针,该指针指向某个函数,满足接受 `doube` 返回 `T` 。
// 比如 f 函数体是 `return g;` 而 `T g(double)` 是另一个函数。
T (*f(int))(double);
// `(*f(int))(double)` is `noptr-declarator parameters-and-qualifiers` .
// `noptr-declarator` : `(*f(int))` , `parameters-and-qualifiers` : `(double)` .
//
// `(*f(int))` is `( ptr-declarator )` .
// `ptr-declarator` : `*f(int)` .
//
// `*f(int)` is `ptr-operator ptr-declarator` .
// `ptr-operator` : `*` , `ptr-declarator` : `f(int)` .
//
// `f(int)` is `noptr-declarator parameters-and-qualifiers` .
// `noptr-declarator` : `f` , `parameters-and-qualifiers` : `(int)` .
//
// `f` is `noptr-declarator` -> f` is `declarator-id` .

// c 大小未指定的数组,是元素为 int 。
int c[] [[maybe_unused]];
// `c[] [[maybe_unused]]` is `noptr-declarator [ constant-expression_opt ] attribute-specifier-seq_opt` .
// `noptr-declarator` : `c` ,
// `constant-expression` : ε ,
// `attribute-specifier-seq` : `[[maybe_unused]]` .

指针操作符

ptr-operator:
  * attribute-specifier-seq_opt cv-qualifier-seq_opt
  & attribute-specifier-seq_opt
  && attribute-specifier-seq_opt
  nested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt

ptr-operator 的例子:

// p 是指向 int 的指针。
int *p;
// `ptr-operator` is `* attribute-specifier-seq_opt cv-qualifier-seq_opt` .
// `attribute-specifier-seq` : ε , `cv-qualifier-seq` : ε .

// cp 是常量指针,指向 int 。
int * [[maybe_unused]] const cp = &x;
// `ptr-operator` is `* attribute-specifier-seq_opt cv-qualifier-seq_opt` .
// `attribute-specifier-seq` : `[[maybe_unused]]` , `cv-qualifier-seq` : `const` .

// r 是 int 的左值引用。
int &r = x;
// `&` is `& attribute-specifier-seq_opt` .
// `attribute-specifier-seq` : ε .

// rr 是 int 的右值引用。
int && [[maybe_unused]] rr = std::move(x);
// `&& [[maybe_unused]]` is `&& attribute-specifier-seq_opt` .
// `attribute-specifier-seq` : `[[maybe_unused]]` .

// cpm 是指向 C 成员 int 的常量指针。
int C::* [[maybe_unused]] const cpm = &C::m;
// `C::* [[maybe_unused]] const` is nested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt` .
// `nested-name-specifier` : `C::` , `attribute-specifier-seq` : `[[maybe_unused]]` , `cv-qualifier-seq` : `const` .

(语言核心)类型

下述类型基本描述语言核心类型,没特别说明不会引入基于语言核心类型实现定义的非语言核心类型。

类型:

  • 是 C++ 标准中的实体。但除了类类型,类型本身并不占有存储空间,而是用于解释一段特定的存储空间。
  • 用于描述对象、引用或函数(或空类型)。
  • 可以分为基础类型和复合类型。

对象类型:简单地说,不描述引用、函数和 cv void 的类型就是对象类型。

基础类型

整数类型

有符号整数类型

signed char
short int
int
long int
long long int

无符号整数类型

unsigned char
unsigned short int
unsigned int
unsigned long int
size_t

implementation-defined,由实现定义的。符合标准,但不同编译器可以有不同实现。

size_t 不是语言核心类型,不是一种新类型。

标准要求 size_t 宽度足以容纳任何对象以字节为单位的大小,基于无符号整数类型定义实现。

字符类型

普通字符类型

char
signed char
unsigned char

char 是一种不同的类型,由实现在 signed char 和 unsigned char 之中选择。

宽字符类型

wchar_t

Unicode 字符类型

char8_t
char16_t
char32_t

布尔类型

由无符号整数类型实现,值是 true or false 。

浮点数类型

float
double
long double

在实现上,float 和 double 主流编译器遵顼 IEEE 标准, long double 在不同平台上不同。

空类型

cv void

空指针类型

应当把它理解为一个独立实现的标签类型

  • 常规的标签类型是依赖空类实现的,但是空指针类型必须由编译器内置实现。
  • 因为编译器必须内置实现语言核心类型,且空指针类型是语言核心类型。
std::nullptr_t

复合类型

数组类型

构造方式

T[N]

函数类型

构造方式

R(Args...)

指针类型

对象指针类型

构造方式

void*
T*

函数指针类型

构造方式

R(*)(Args...)

引用类型

左值引用类型

构造方式

T&

右值引用类型

构造方式

T&&

类类型

类

构造方式

class

结构体

构造方式

struct

struct 相对于 class 的区别:

  • 在底层上,在访问和继承的默认权限上, struct 是 public 限定的而 class 是 private 限定的。
  • 在工程上人为规范了一些不同用途。比如 Google 的 C++ 风格指南中,规定了如果一个类被设计为只存储纯粹的被动使用的数据,应该用 struct ,否则用 class 。

联合体

构造方式

union

联合体 union 相较于其他类类型的特殊之处是:

  • 一个 union 对象的存储空间是复用的,依赖编译器按照不同类型解释同一块内存。
  • 一个 union 对象的实际内存占据依赖其内部字节宽度最大的类型。

枚举类型

构造方式

enum
enum class

总得来说,enum class 相较于 enum 是安全的枚举类型。

它们之间的区别很多,基本上都是基于安全考量和体现的区别,期间最显著的两个区别是:

  • enum class 是有作用域枚举,enum 是无作用域枚举。
  • enum class 的枚举值不能隐式转化成整数,enum 的枚举值可以隐式转换为整数。

成员指针类型

普通指针类型的 * 往右靠是惯用写法,成员指针类型则是往左靠惯用。放中间是合法但工程上不推荐的。

成员指针类型:

  • 用于标识非静态类成员。核心用途是延迟绑定,先取得一个成员信息,等有具体对象时解引用。
  • 本质上是基于 this 指针的偏移量,并不保存绝对地址。
  • 不能指向类的静态成员、引用类型成员、cv void 。

数据成员指针类型

T C::* D

函数成员指针类型

T C::* D(Args...)
T (C::* D)(Args...)

字面量

字面量和基础类型的分类有些相似,这里可以展开一下:

literal:
    integer-literal
    character-literal
    floating-point-literal
    string-literal
    boolean-literal
    pointer-literal
    user-defined-literal

literal 的例子:

/// >>> integer-literal >>>
42  // decimal-literal, 无后缀,类型 int
0x2A  // hexadecimal-literal, 值 42
052  // octal-literal, 值 42
0b101010  // binary-literal, 值 42
42u  // unsigned-suffix, 类型 unsigned int
42LL  // long-long-suffix, 类型 long long
1'000'000  // 单引号作为数字分隔符,值为 1000000
/// <<< integer-literal <<<

/// >>> character-literal >>>
'a'  // 无前缀,类型 char
L'a'  // L 前缀,类型 wchar_t
u'a'  // u 前缀,类型 char16_t
U'a'  // U 前缀,类型 char32_t
u8'a'  // u8 前缀,类型 char8_t
'\n'  // 简单转义序列
'\x41'  // 十六进制转义,值为 'A'
'\101'  // 八进制转义,值为 'A'
/// <<< character-literal <<<

/// >>> floating-point-literal >>>
3.14  // 无后缀,类型 double
3.14f  // f 后缀,类型 float
3.14L  // L 后缀,类型 long double
3.14e-2  // 指数部分,值为 0.0314
1.602'176'565e-19  // 单引号分隔,值为 1.602176565e-19
0xC.68p+2  // 十六进制浮点,值为 49.625
/// <<< floating-point-literal <<<

/// >>> string-literal >>>
"hello"  // 普通字符串字面量,类型 const char[6]
L"hello"  // 宽字符串字面量,类型 const wchar_t[6]
u8"hello"  // UTF-8 字符串字面量,类型 const char8_t[6]
u"hello"  // UTF-16 字符串字面量,类型 const char16_t[6]
U"hello"  // UTF-32 字符串字面量,类型 const char32_t[6]
R"(a\b)"  // 原始字符串,等价于 "a\\b"
R"delim((a|b))delim"  // 使用 delim 作为分隔符的原始字符串
"hello" " " "world"  // 相邻字符串字面量拼接
/// <<< string-literal <<<

/// >>> boolean-literal >>>
true  // 类型 bool,值 true
false  // 类型 bool,值 false
/// <<< boolean-literal <<<

/// >>> pointer-literal >>>
nullptr  // 类型 std::nullptr_t,空指针常量
/// <<< pointer-literal <<<

/// >>> user-defined-literal >>>
// 假设已定义 operator""_km
12_km  // 用户定义整数字面量,等价于 operator""_km(12ULL)
1.5_km  // 用户定义浮点字面量
"hello"_km  // 用户定义字符串字面量
'a'_km  // 用户定义字符字面量
/// <<< user-defined-literal <<<
// Google 建议,除非项目文档约定,否则不要使用用户自定义字面量,它会使读者不能直观理解代码

这里有一个比较重要的性质,字符串字面量是左值,其他字面量是纯右值。

  • 字符串字面量是 char 数组,指令级架构中没有针对 char 数组编码,它具有静态存储期,现代编译器把它放在 .rodate 节。
  • 非字符串字面量直接放在指令中,用于初始化一个对象或位域,或者计算某个运算符的操作数的值。
int x = 42;  // 42 初始化对象 x 。
int y = 42 + 1;  // 42 不指代一个对象也不初始化一个对象,仅用于计算运算符 + 的左操作数的值。
posted @ 2026-10-03 21:42  03Goose  阅读(6)  评论(0)    收藏  举报