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 不指代一个对象也不初始化一个对象,仅用于计算运算符 + 的左操作数的值。
浙公网安备 33010602011771号