运算、运算符¶
为什么不使用 operator
这样的关键字?
我认为 fn \add(MyInt rhs) -> MyInt
这样的写法与 auto operator+(MyInt rhs) -> MyInt
这样的写法相比更加简洁,也容易理解。
优先顺序:
- 后缀
- 前缀
- 二元
- 乘、除、取模
- 加、减
- 移位
- 按位与
- 按位异或
- 按位或
- 逻辑与
- 逻辑异或
- 逻辑或
- 比较
- 赋值
- 三元
前缀运算¶
运算 | 名称 | 解释 | 注释 |
---|---|---|---|
+a | pos | 正号 | |
-a | neg | 负号 | |
~a | bnot | 按位取反 | |
!a | not | 逻辑取反 | |
++a | linc | 前缀自增 | |
--a | ldec | 前缀自减 | |
*a | deref | 解引用 | |
&a | addr | 取地址 | 不可重载 |
后缀运算¶
运算 | 名称 | 解释 |
---|---|---|
a++ | rinc | 后缀自增 |
a-- | rdec | 后缀自减 |
a[b] | index | 数组索引 |
如果重载索引:
fn \index(usize i) -> int {
return *(array + i);
}
二元运算¶
复合赋值运算符¶
运算 | 名称 | 解释 |
---|---|---|
a += b | iadd | 加并赋值 |
a -= b | isub | 减并赋值 |
a *= b | imul | 乘并赋值 |
a /= b | idiv | 除并赋值 |
a %= b | imod | 取模并赋值 |
a &= b | iband | 按位与并赋值 |
a |= b | ibor | 按位或并赋值 |
a ^= b | ibxor | 按位异或并赋值 |
a <<= b | ishl | 左移并赋值 |
a >>= b | ishr | 右移并赋值 |
比较运算符¶
运算 | 名称 | 解释 | 注释 |
---|---|---|---|
a == b | eq | 等于 | |
a != b | ne | 不等于 | |
a < b | lt | 小于 | |
a <= b | le | 小于等于 | |
a > b | gt | 大于 | |
a >= b | ge | 大于等于 | |
a <=> b | cmp | 比较 | |
a === b | seq | 严格相等 | 可重载 |
a !== b | sne | 严格不等 | 可重载 |
a $== b | beq | 二进制相等 | 不可重载 |
a ~== b | bne | 二进制不等 | 不可重载 |
a &== b | same | 相同 | 不可重载 |
a &!= b | diff | 不同 | 不可重载 |
-
<=>
比较运算符,返回 -1、0、1 表示小于、等于、大于。对于重载,实现
<=>
即可,==
、!=
、<
、<=
、>
、>=
会自动调用<=>
。当然你也可以手动实现其它函数,Lumos 会自动将未实现的比较运算符转换为已实现的。
-
===
!==
严格相等,仅当两个对象的类型和值都相同时返回 true。你没听错,严格相等是可重载的,但你不能重载不同类型的严格相等运算。
对于严格相等运算,你可以使用与==
不同的逻辑。struct MyInt { int value; fn \eq(MyInt rhs) -> bool { return value == rhs.value; } fn \seq(MyInt rhs) -> bool { return value === rhs.value; } fn \seq(float rhs) -> bool; // 这是不行的,不同类型间的严格相等不可重载 }
$==
~==
二进制相等,仅当两个对象的二进制表示完全相同时返回 true。
注意二进制表示的比较包含位数,例如i32 1 $== i64 1
为 false。
-
&==
&!=
判断左右是否是相同的对象,不比较值。
类似于&a == &b
,任意一边为字面量时始终得到 false。
&==
与&!=
是唯一可以比较引用的运算符。int a = 1; int& b = a; int c = a; assert(a &== b); // true assert(a &== c); // false
其他运算符¶
运算 | 名称 | 解释 | 注释 |
---|---|---|---|
a ? b : c | ternary | 三元运算符 | 不可重载 |
(type)a | cast | 类型转换 | 不可重载 |
sizeof(a) | sizeof | 计算大小 | 不可重载 |
typeof(a) | typeof | 获取类型 | 不可重载 |
typenameof(a) | typenameof | 获取类型名称 | 不可重载 |
a->b | arrow | 自定义成员访问 | 都自定义了当然可重载啦 |
注意这里和 C/C++ 不同,,
和 .
不是运算符,而作为分隔符。
实际上你也不能够重载它们所以效果没有什么区别。
-
typeof
是唯一运算结果为类型的运算符,它可以直接当作类型使用。int a = 1; typeof(a) b = 2; // 相当于 int b = 2;
typenameof
的运算结果为字符串。
位运算符¶
注意浮点数不能进行位运算,如果你实在需要位运算,请将浮点数转换为 bits
类型,而不是像 C 中那样使用整数指针。
f32 value = (b32)123.456 << 1;
// 而不是
f32 value = 123.456;
u32* ptr = &value;
*ptr <<= 1;
¶
- 交换律:
a + b
与b + a
等效 - 结合律:
(a + b) + c
与a + (b + c)
等效
算术运算符¶
我们约定以下运算符等效,其互换不应影响程序的行为
a = a 运算符 b;
a 运算符= b;
a = a + b
与 a += b
a = a - b
与 a -= b
a = a * b
与 a *= b
a = a / b
与 a /= b
依此类推
自增、自减¶
a++
与 a += 1
a--
与 a -= 1
a if expr else b
与 expr ? a : b
a and b
与 a && b
a or b
与 a || b
not a
与 !a
逻辑运算符¶
&&
||
^^
符合交换律,即:
a && b
与 b && a
等效
a || b
与 b || a
等效
依此类推
重载运算符¶
与 C++ 中 opeator+
一类的写法不同,Lumos 使用反斜杠加运算名来代表运算符,例如 \add
。
运算 | 名称 | 运算 | 名称 | 运算 | 名称 |
---|---|---|---|---|---|
a + b | add | a & b | band | a && b | and |
a - b | sub | a | b | bor | a || b | or |
a * b | mul | a ^ b | bxor | a ^^ b | xor |
a / b | div | a << b | shl | a == b | eq |
a % b | mod | a >> b | shr | a != b | ne |
-a | neg | ~a | bnot | !a | not |
a | get | a < b | lt | a <= b | le |
a = b | set | a > b | gt | a >= b | ge |
a++ | rinc | a-- | rdec | a <=> b | cmp |
++a | linc | --a | ldec | *a | deref |
所有类似 a += b
的运算名称都是 i
加上对应的运算符名称,例如 add
的增量运算符是 iadd
。
\
开头的运算符¶
有些运算符并没有对应的符号版本,但也非常重要,这些运算符以 \
开头。
为什么不使用特定名称的函数?
我认为不应该出现语法或优化和一些非标准库的特定名称函数有关的情况,所以我将它们定义为运算符。
\hash
\next
\prev