跳转至

运算、运算符

为什么不使用 operator 这样的关键字?

我认为 fn \add(MyInt rhs) -> MyInt 这样的写法与 auto operator+(MyInt rhs) -> MyInt 这样的写法相比更加简洁,也容易理解。

优先顺序:

  1. 后缀
  2. 前缀
  3. 二元
    1. 乘、除、取模
    2. 加、减
    3. 移位
    4. 按位与
    5. 按位异或
    6. 按位或
    7. 逻辑与
    8. 逻辑异或
    9. 逻辑或
    10. 比较
    11. 赋值
  4. 三元

前缀运算

运算 名称 解释 注释
+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 + bb + a 等效
  • 结合律:(a + b) + ca + (b + c) 等效

算术运算符

我们约定以下运算符等效,其互换不应影响程序的行为

a = a 运算符 b;
a 运算符= b;

a = a + ba += b
a = a - ba -= b
a = a * ba *= b
a = a / ba /= b
依此类推

自增、自减

a++a += 1
a--a -= 1

a if expr else bexpr ? a : b
a and ba && b
a or ba || b
not a!a

逻辑运算符

&& || ^^ 符合交换律,即:

a && bb && a 等效
a || bb || 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