2095 字
10 分钟
仓颉文档阅读-开发指南IV: 函数(VI) - const 函数和常量求值
NOTE

阅读文档版本:

语言规约 Cangjie-0.53.18-Spec

具体开发指南 Cangjie-LTS-1.0.4

在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证

有条件当然可以直接 配置 Canjie-SDK

WARNING

博主在此之前, 基本只接触过 C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与 C/C++中的相似概念作类比, 见谅

且, 本系列是文档阅读, 而不是仓颉的零基础教学, 所以如果要跟着阅读的话最好有一门编程语言的开发经验

WARNING

在阅读仓颉编程语言的开发指南之前, 已经大概阅读了一遍 仓颉编程语言的语言规约, 已经对仓颉编程语言有了一个大概的了解

所以在阅读开发指南时, 不会对类似: 类、函数、结构体、接口等解释起来较为复杂名称 做出解释

此样式内容, 表示文档原文内容

函数#

const函数和常量求值#

常量求值 允许某些特定形式的表达式 在编译时求值,可以减少程序运行时需要的计算

本章主要介绍常量求值的使用方法与规则

常量求值主要的作用就是 编译器求值, 减少运行时消耗

const上下文与const表达式#

const上下文是指 const变量初始化表达式,这些表达式始终在编译时求值

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

const表达式具备了可以在编译时求值的能力

满足如下规则的表达式是const表达式:

  1. 数值类型、BoolUnitRuneString类型的字面量(不包含插值字符串)

  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. 数值类型、BoolUnitRuneString类型的算术表达式、关系表达式、位运算表达式,所有操作数都必须是const表达式

  8. ifmatchtrythrowreturnisas

    这些表达式内的表达式必须都是const表达式

  9. const表达式的成员访问(不包含属性的访问),tuple的索引访问

  10. const initconst函数中的thissuper表达式

  11. const表达式的const实例成员函数调用,且所有实参必须都是const表达式

注意:

当前编译器实现暂不支持throw作为const表达式使用

仓颉中, 存在const上下文的概念

什么是const上下文呢?

即, const变量初始化 所用的表达式

但, 仓颉中 并不是所有表达式都可以给const变量做初始化, 只有const表达式, 才能给const变量做初始化

这也就是说const上下文中, 实际只能出现const表达式, 只有这样才能保证编译时求值

仓颉中对const表达式的定义在文档中已经列出了

有 常用数据类型的字面量, const变量, const函数的形参以及函数体内的局部变量, const函数等等

const函数#

const函数是一类特殊的函数,这些函数具备了可以在编译时求值的能力

const上下文中调用这种函数时,这些函数会在编译时执行计算

而在其他非const上下文,const函数会和普通函数一样在运行时执行

下例是一个计算平面上两点距离的const函数,distance中使用let定义了两个局部变量dxdy

struct Point {
const Point(let x: Float64, let y: Float64) {}
}
const func distance(a: Point, b: Point) {
let dx = a.x - b.x
let dy = a.y - b.y
(dx**2 + dy**2)**0.5
}
main() {
const a = Point(3.0, 0.0)
const b = Point(0.0, 4.0)
const d = distance(a, b)
println(d)
}

编译运行输出:

5.000000

仓颉中的const函数, 在const上下文中调用时, 编译时执行运算

在非const上下文中调用时, const函数就被当作普通函数调用

需要注意:

  1. const函数声明必须使用const修饰

  2. 全局const函数和static const函数中只能访问const声明的外部变量,包含const全局变量、const静态成员变量,其他外部变量都不可访问

    const init函数和const实例成员函数除了能访问const声明的外部变量,还可以访问当前类型的实例成员变量

  3. const函数中的表达式都必须是const表达式,const init函数除外

  4. const函数中可以使用letconst声明新的局部变量

    不支持var

  5. const函数中的参数类型和返回类型没有特殊规定

    如果该函数调用的实参不符合const表达式要求,那这个函数调用不能作为const表达式使用,但仍然可以作为普通表达式使用

  6. const函数不一定都会在编译时执行,例如可以在非const函数中运行时调用

  7. const函数与非const函数重载规则一致

  8. 数值类型、BoolUnitRuneString类型 和enum支持定义const实例成员函数

  9. 对于structclass只有定义了const init才能定义const实例成员函数

    class中的const实例成员函数不能是open

    struct中的const实例成员函数不能是mut

另外,接口中也可以定义const函数,但会受到以下规则限制:

  1. 接口中的const函数,实现类型必须也用const函数才算实现接口

  2. 接口中的非const函数,实现类型使用const或非const函数都算实现接口

  3. 接口中的const函数与接口的static函数一样,只有在该接口作为泛型约束的时候,受约束的泛型变元或变量才能使用这些const函数

在下面的例子中,在接口I里定义了两个const函数,类A实现了接口I,泛型函数g的形参类型上界是I

interface I {
const func f(): Int64
const static func f2(): Int64
}
class A <: I {
public const func f() { 0 }
public const static func f2() { 1 }
const init() {}
}
const func g<T>(i: T) where T <: I {
return i.f() + T.f2()
}
main() {
println(g(A()))
}

编译执行上述代码,输出结果为:

1

仓颉中, const函数的就是被const修饰的函数:

const func function() {}

不过, const函数的定义存在许多的限制, 具体限制不用太多介绍, 文档中都有列出

const init#

如果一个structclass定义了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 initconst函数的区别是const init内允许对实例成员变量进行赋值(需要使用赋值表达式)

const init就是类或结构体的const构造函数

如果要定义const init, 那么:

  1. 不允许存在var成员变量

  2. 父类必须也存在const init

  3. 所有实例成员变量的初始值, 必须是const表达式