仓颉语言学习笔记(2)重载、const

仓颉语言学习笔记(2)重载、const

函数重载

一个作用域中,一个函数名对应多个函数定义,成为函数重载

  • 函数名相同,函数参数不同
func f(a: Int64) {
    println("Int64:" + a.toString())
}

func f(a: Float64) {
    println("Float64:" + a.toString())
}

func f(a: Int64, b: Float64) {
    println("Int64 and Float64:" + a.toString() + ", " + b.toString())
}

main() {
    f(42)
    f(3.14)
    f(42, 3.14)
}
  • 同一个类内的两个构造函数参数不同
class C {
    var a: Int64
    var b: Float64

    public init(a: Int64, b: Float64) {
        this.a = a
        this.b = b
    }

    public init(a: Int64) {
        b = 0.0
        this.a = a
    }

    public func display() {
        println("a: ${a}, b: ${b}")
    }
}

main() {
    let c1 = C(10, 20.5)
    let c2 = C(15)
    c1.display()
    c2.display()
}
  • 同一个类内的主构造函数和 init 构造函数参数不同(认为主构造函数和 init 函数具有相同的名字,主构造函数只能有一个)
class C {
    var a: Int64
    var b: Float64
    
    C(a: Int64, b: Float64) {
        this.a = a
        this.b = b
    }

    init(a: Int64) {
        this.a = a
        this.b = 0.0
    }

    func display(): String {
        return a.toString() + ", " + b.toString()
    }
}

main() {
    let c = C(42)
    println(c.display())
}
  • 两个函数名相同,参数不同的函数定义在不同的作用域,在两个函数都可见的作用域中构成重载。
func f(a: Int64) {
    return "Int64: " + a.toString()
}

func g() {
    func f(a: Float64) {
        return "In g Float64: " + a.toString()
    }
    func display() {
        println(f(100))
        println(f(3.14))
    }
    display()
}

main() {
    g()
}
// Int64: 100
// In g Float64: 3.140000
  • 如果子类中存在与父类同名的函数,并且函数的参数类型不同
open class Base {
    public func f(a: Int64) {
        println("In Base Int64: ${a}")
    }
}

class Sub <: Base {
    public func f(a: Float64) {
        println("In Sub Float64: ${a}")
    }
}

main() {
    var b: Base = Sub()
    b.f(100)
    var s: Sub = Sub()
    s.f(3.14)
}
// In Base Int64: 100
// In Sub Float64: 3.140000

函数重载决议

函数调用时,所有可被调用的函数(是指当前作用域可见且能通过类型检查的函数)构成候选集,候选集中有多个函数,究竟选择候选集中哪个函数,需要进行函数重载决议。

  • 优先选择作用域级别高的作用域内的函数。在嵌套的表达式或函数中,越是内层作用域级别越高。
open class Base {}
class Sub <: Base {}

func outer() {
    func g(a: Sub) {
        print("1")
    }

    func inner() {
        func g(a: Int64) {
            print("2")
        }

        g(Sub())
    }

    inner()
}

main() {
    outer()
}
  • 如果作用域级别相对最高的仍有多个函数,则需要选择最匹配的函数(对于函数 f 和 g 以及给定的实参,如果 f 可以被调用时 g 也总是可以被调用的,但反之不然,则称 f 比 g 更匹配)。如果不存在唯一最匹配的函数,则报错。
  • 子类和父类认为是同一作用域。

操作符重载

  • 定义操作符函数时需要在 func 关键字前面添加 operator 修饰符;
  • 操作符函数的参数个数需要匹配对应操作符的要求(详见附录操作符);
  • 操作符函数只能定义在 class、interface、struct、enum 和 extend 中;
  • 操作符函数具有实例成员函数的语义,所以禁止使用 static 修饰符;
  • 操作符函数不能为泛型函数。

定义操作符函数有两种方式:

  1. 对于可以直接包含函数定义的类型 (包括 struct、enum、class 和 interface ),可以直接在其内部定义操作符函数的方式实现操作符的重载。

  2. 使用 extend 的方式为其添加操作符函数,从而实现操作符在这些类型上的重载。对于无法直接包含函数定义的类型(是指除 struct、class、enum 和 interface 之外其他的类型)或无法改变其实现的类型,比如第三方定义的 struct、class、enum 和 interface,只能采用这种方式(参见扩展);

  3. 一元操作符重载,没有参数

    class C {
        var a: Int64 = 10
        public operator func !() {
            return "!" + a.toString()
        }
    }
    
    main() {
        var c = C()
        print(!c)
    }
    
  4. 二元操作符重载,操作符有一个参数

    class C {
        var str: String
        C(str!: String = "hello") {
            this.str = str
        }
        operator func + (right: C): C {
            return C(str: this.str + right.str)
        }
        func display() {
            println(this.str)
        }
    }
    
    main() {
        let c1 = C(str: "Cangjie ")
        let c2 = C(str: "Demo")
        (c1 + c2).display()
    }
    
  5. 索引操作符([])分为取值 let a = arr[i] 和赋值 arr[i] = a 两种形式,它们通过是否存在特殊的命名参数 value 来区分不同的重载。索引操作符重载不要求同时重载两种形式,可以只重载赋值不重载取值,反之亦可。

    索引操作符取值形式 [] 内的参数序列对应操作符重载的非命名参数,可以是 1 个或多个,可以是任意类型。不可以有其它命名参数。返回类型可以是任意类型。

    赋值形式 [] 内的参数序列对应操作符重载的非命名参数,可以是 1 个或多个,可以是任意类型。= 右侧的表达式对应操作符重载的命名参数,有且只能有一个命名参数,该命名参数的名称必须是 value, 不能有默认值,value 可以是任意类型。返回类型必须是 Unit 类型。需要注意的是,value 只是一种特殊的标记,在索引操作符赋值时并不需要使用命名参数的形式调用。

    class C {
        var a = 1
        var b = 2
        var c = 3
        // 赋值形式
        operator func [](index: Int64, value!: Int64){
            if(index == 0){
                a = value
            } else if(index == 1){
                b = value
            } else if(index == 2){
                c = value
            }
        }
        // 取值形式
        operator func [](index: Int64): Int64 {
            if(index == 0){
                return a
            } else if(index == 1){
                return b
            } else if(index == 2){
                return c
            }
            return -1
        }
    }
    
    main() {
        var obj = C()
        println("${obj[1]}") // 输出2
        obj[1] = 20
        println("${obj[1]}") // 输出20
    }
    
  6. 函数调用操作符(())重载函数,输入参数和返回值类型可以是任意类型。不能使用 this 或 super 调用 () 操作符重载函数。

    class C {
        operator func ()() {
            return 1
        }
    }
    
    main() {
        let c = C()
        println(c())
    }
    

const变量

const 变量是一种特殊的变量,它以关键字 const 修饰,定义在编译时完成求值,并且在运行时不可改变的变量。

let c: Int64 = 10 // 顶层let变量必须初始化

main() {
    const a: Int64 = 20 // const变量必须初始化
    let b: Int64
    b = 20 // let变量可以先声明后赋值
}

const上下文与const表达式

const 上下文是指 const 变量初始化表达式,这些表达式始终在编译时求值。因此需要对 const 上下文中允许的表达式加以限制,避免修改全局状态、I/O 等副作用,确保其可以在编译时求值。

const 表达式具备了可以在编译时求值的能力。满足如下规则的表达式是 const 表达式:

  1. 数值类型、Bool、Unit、Rune、String 类型的字面量(不包含插值字符串)。
  2. 所有元素都是 const 表达式的 Array 字面量(不能是 Array 类型,可以使用 VArray 类型),tuple 字面量。
  3. const 变量,const 函数形参,const 函数中的局部变量。
  4. const 函数,包含使用 const 声明的函数名、符合 const 函数要求的 lambda、以及这些函数返回的函数表达式。
  5. const 函数调用(包含 const 构造函数),该函数的表达式必须是 const 表达式,所有实参必须都是 const 表达式。
  6. 所有参数都是 const 表达式的 enum 构造器调用,和无参数的 enum 构造器。
  7. 数值类型、Bool、Unit、Rune、String 类型的算术表达式、关系表达式、位运算表达式,所有操作数都必须是 const 表达式。
  8. if、match、try、控制转移表达式(包含 return、break、continue、throw)、is、as。这些表达式内的表达式必须都是 const 表达式。
  9. const 表达式的成员访问(不包含属性的访问),tuple 的索引访问。
  10. const init 和 const 函数中的 this 和 super 表达式。
  11. const 表达式的 const 实例成员函数调用,且所有实参必须都是 const 表达式。

const函数

const 函数是一类特殊的函数,这些函数具备了可以在编译时求值的能力。在 const 上下文中调用这种函数时,这些函数会在编译时执行计算。而在其它非 const 上下文,const 函数会和普通函数一样在运行时执行。

const init

如果一个 struct 或 class 定义了 const 构造器,那么这个 struct/class 实例可以用在 const 表达式中。

  1. 如果当前类型是 class,则不能具有 var 声明的实例成员变量,否则不允许定义 const init 。如果当前类型具有父类,当前的 const init 必须调用父类的 const init(可以显式调用或者隐式调用无参const init),如果父类没有 const init 则报错。
  2. 当前类型的实例成员变量如果有初始值,初始值必须要是 const 表达式,否则不允许定义 const init。
  3. const init 内可以使用赋值表达式对实例成员变量赋值,除此以外不能有其它赋值表达式。

const init 与 const 函数的区别是 const init 内允许对实例成员变量进行赋值(需要使用赋值表达式)。

class C {
    let name: String
    var num: Int64 = 0 // 错误:定义了const构造器后,不能具有var声明的实例成员变量
    const C(str: String) {
        name = str
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值