[size=large]7 嵌套函数
函数可以被嵌套在其它函数内部:
int bar(int a)
{
int foo(int b)
{
int abc() { return 1; }
return b + abc();
}
return foo(a);
}
void test()
{
int i = bar(3); // i 被赋值为 4
}
嵌套函数只在其名字 处在作用域中 时才能被访问。
void foo()
{
void A()
{
B(); // 错误,B() 被向前引用了
C(); // 错误,C 未定义
}
void B()
{
A(); // 正确,在作用域内
void C()
{
void D()
{
A(); // 正确
B(); // 正确
C(); // 正确
152
第 15 章 函数
D(); // 正确
}
}
}
A(); // 正确
B(); // 正确
C(); // 错误,C 未定义
}
和:
int bar(int a)
{
int foo(int b) { return b + 1; }
int abc(int b) { return foo(b); } // 正确
return foo(a);
}
void test()
{
int i = bar(3); // 正确
int j = bar.foo(3); // 错误,bar.foo 不可见
}
嵌套函数可以访问由词法上封闭函数所定义的变量和其它符号。这里的“访问”包含既可以
读,也可以写两种能力。
int bar(int a)
{ int c = 3;
int foo(int b)
{
b += c; // 4 被加到 b 上
c++; // bar.c 现在为 5
return b + c; // 返回 12
}
c = 4;
int i = foo(a); // i 被设置为 12
return i + c; // 返回 17
}
void test()
{
int i = bar(3); // i 被赋值为 17
}
这种访问能力能够跨越多重嵌套:
int bar(int a)
{ int c = 3;
int foo(int b)
{
int abc()
{
return c; // 访问 bar.c
}
return b + c + abc();
}
return foo(3);
}
静态嵌套函数不能访问外围函数的任何堆栈变量,但能访问静态变量。这种行为同静态成员
函数类似。
int bar(int a)
{ int c;
static int d;
static int foo(int b)
{
b = d; // 正确
b = c; // 错误,foo() 不能访问 bar() 的堆栈帧
return b + 1;
}
return foo(a);
}
函数可以嵌套在成员函数内:
struct Foo
{ int a;
int bar()
{ int c;
int foo()
{
return c + a;
}
return 0;
}
}
嵌套结构和嵌套类的成员函数不能访问外围函数的堆栈变量,但是能访问其他的符号:
void test()
{ int j;
static int s;
struct Foo
{ int a;
int bar()
{ int c = s; // 正确,s 是静态的
int d = j; // 错误,不能访问 test() 的堆栈帧
int foo()
{
int e = s; // 正确,s 是静态的
int f = j; // 错误,不能访问 test() 的堆栈帧
return c + a; // 正确,bar() 的堆栈帧是可访问的,
// 通过指向 Foo.bar() 的 this 指针
// 可以访问 Foo 的成员
}
return 0;
}
}
}
嵌套函数总是使用 D 函数链接类型。
同模块级的声明不同,函数作用域的声明会按照声明的顺序处理。这意味着连个嵌套函数不
能互相调用:
void test()
{
void foo() { bar(); } // 错误,bar 没有被定义
void bar() { foo(); } // 正确
}
解决的方法是使用委托:
void test()
{
void delegate() fp;
void foo() { fp(); }
void bar() { foo(); }
fp = &bar;
}
未来的方向 这个限制可能会被删除。
7.1 [color=red]委托、函数指针和动态闭包[/color]
[color=red]Delegates, Function Pointers, and Closures(2.014,不动态了?)[/color]
函数指针可以指向一个静态嵌套函数:
int function() fp;
void test()
{ static int a = 7;
static int foo() { return a + 3; }
fp = &foo;
}
void bar()
{
test();
int i = fp(); // i 被设置为 10
}
委托可以用非静态嵌套函数赋值:
int delegate() dg;
void test()
{ int a = 7;
int foo() { return a + 3; }
dg = &foo;
int i = dg(); // i 被设置为 10
}
[color=blue]但是,一旦声明堆栈变量的函数退出了,堆栈变量就不再有效;同样的,
指向堆栈变量的指针也不再有效:
int* bar()
{ int b;
test();
int i = dg(); // 错误,test.a 不再存在
return &b; // 错误,bar.b 在 bar() 退出后不再有效
}[/color]
[color=red]上面这段被改成:2.014
The stack variables referenced by a nested function are still valid even after the function exits (this is different from D 1.0). This is called a closure. Returning addresses of stack variables, however, is not a closure and is an error.
int* bar()
{ int b;
test();
int i = dg(); // ok, test.a is in a closure and still exists
return &b; // error, bar.b not valid after bar() exits
}[/color]
非静态嵌套函数的委托包括两块数据:指向外围函数堆栈帧的指针(叫做 帧指针)和函数
的地址。与此类似,结构/类的非静态成员函数委托由 this 指针和成员函数的地址组成。这
两种形式的委托可以互相转换,实际上它们具有相同的类型:
struct Foo
{ int a = 7;
int bar() { return a; }
}
int foo(int delegate() dg)
{
return dg() + 1;
}
void test()
{
int x = 27;
int abc() { return x; }
Foo f;
int i;
i = foo(&abc); // i 被设置为 28
i = foo(&f.bar); // i 被设置为 8
}
环境和函数的结合被称作 动态闭包。
委托的 .ptr 特性会返回 帧指针 值,而类型为 void*。
委托的 .funcptr 属性会返回 帧指针 值,类型为函数类型。
未来的方向 函数指针和委托或能被合并成一种通用的语法并且彼此可以互相更改。
7.2 [color=red]匿名函数和匿名委托[/color]
参看 函数字法。(参见 操作符与表达式 3 --> 基本表达式 21.10 函数字法)
8 main() 函数
对于控制台程序,main() 提供入口点。它会在所有的模块初始化,以及所有的单元测试都
被运行以后被调用。在它返回以后,所有的模块析构函数会得到运行。main() 必须使用下
列形式之一进行声明:
void main() { ... }
void main(char[][] args) { ... }
int main() { ... }
int main(char[][] args) { ... }
9 编译时 函数执行
有些函数的子集可以在编译时执行。这个在常量合拢(constant folding)需要包括递归
(recursion)和循环时很有用。为了在编译时可以被执行,此函数必须符号下面的标准
(criteria):
1. 函数形参都必须是:
• 整数文字
• 浮点文字
• 字符文字
• 字符串
• 数组文字,即所有成员都是这个列表的条目
• 关联数组文字,即所有成员都是这个列表的条目
• 结构文字,即所有成员都是这个列表的条目
• const 变量使用这个列表的一个成员进行初始化
2. 函数参数不能是参数可变型或者是 lazy 型
3. 函数不能被嵌套或同步(synchronized)
4. 函数不能是非静态(non-static)成员,即它不能有 this 指针
5. 在函数里的表达式不能:
• 抛出异常
• 使用指针、委托、非常量数组或者类
• 引用任何全局状态(state)或变量
• 引用任何局部变量
• 进行 new 或 delete 操作
• 调用任何在编译时不可执行的函数
6. 不允许下列语句类型:
• synchronized 语句
• throw 语句
• with 语句
• scope 语句
• try-catch-finally 语句
• 带标号的 break 和 continue 语句
7. 作为特殊情况,下列特性可以在编译时执行:
.dup
.length
.keys
.values
为了在编译时执行,函数必须出现在它必须那样执行的环境里,
比如:
• 静态变量的初始化
• 静态数组的维
• 用于模板值参(value parameter)的形式参数
template eval( A... )
{
const typeof(A[0]) eval = A[0];
}
int square(int i) { return i * i; }
void foo()
{
static j = square(3); // 编译时
writefln(j);
writefln(square(4)); // 运行时
writefln(eval!(square(5))); // 编译时
}
在编译时执行函数会比在运行时的执行花费更长的时间。如果函数进行无限循环状态,那么
它就会在编译时挂起(hang)(而不会在运行时挂起)。
在编译时执行的函数在下列场景下会给出跟运行时不同的结果:
• 浮点计算可能会以比运行时更高的精度进行
• 依赖于实现所定义的求值顺序
• 使用未初始化的变量
这些场景都跟不同优化设置影响会影响结果那样的场景一样。
9.1 字符串 混入和编译时的 函数执行
所有在编译时执行的函数都必须是在运行时可执行的。一个函数的编译时求值所完成的是跟
在运行时运行函数一样的。即表示函数的语义不能依赖于函数的编译时得到的值。
例如:
int foo(char[] s)
{
return mixin(s);
}
const int x = foo("1");
它就是非法的,因为不能为 foo() 生成运行时代码。函数模板用于实现此类事情会是种比较
合适的方法。
[/size]
.........
函数可以被嵌套在其它函数内部:
int bar(int a)
{
int foo(int b)
{
int abc() { return 1; }
return b + abc();
}
return foo(a);
}
void test()
{
int i = bar(3); // i 被赋值为 4
}
嵌套函数只在其名字 处在作用域中 时才能被访问。
void foo()
{
void A()
{
B(); // 错误,B() 被向前引用了
C(); // 错误,C 未定义
}
void B()
{
A(); // 正确,在作用域内
void C()
{
void D()
{
A(); // 正确
B(); // 正确
C(); // 正确
152
第 15 章 函数
D(); // 正确
}
}
}
A(); // 正确
B(); // 正确
C(); // 错误,C 未定义
}
和:
int bar(int a)
{
int foo(int b) { return b + 1; }
int abc(int b) { return foo(b); } // 正确
return foo(a);
}
void test()
{
int i = bar(3); // 正确
int j = bar.foo(3); // 错误,bar.foo 不可见
}
嵌套函数可以访问由词法上封闭函数所定义的变量和其它符号。这里的“访问”包含既可以
读,也可以写两种能力。
int bar(int a)
{ int c = 3;
int foo(int b)
{
b += c; // 4 被加到 b 上
c++; // bar.c 现在为 5
return b + c; // 返回 12
}
c = 4;
int i = foo(a); // i 被设置为 12
return i + c; // 返回 17
}
void test()
{
int i = bar(3); // i 被赋值为 17
}
这种访问能力能够跨越多重嵌套:
int bar(int a)
{ int c = 3;
int foo(int b)
{
int abc()
{
return c; // 访问 bar.c
}
return b + c + abc();
}
return foo(3);
}
静态嵌套函数不能访问外围函数的任何堆栈变量,但能访问静态变量。这种行为同静态成员
函数类似。
int bar(int a)
{ int c;
static int d;
static int foo(int b)
{
b = d; // 正确
b = c; // 错误,foo() 不能访问 bar() 的堆栈帧
return b + 1;
}
return foo(a);
}
函数可以嵌套在成员函数内:
struct Foo
{ int a;
int bar()
{ int c;
int foo()
{
return c + a;
}
return 0;
}
}
嵌套结构和嵌套类的成员函数不能访问外围函数的堆栈变量,但是能访问其他的符号:
void test()
{ int j;
static int s;
struct Foo
{ int a;
int bar()
{ int c = s; // 正确,s 是静态的
int d = j; // 错误,不能访问 test() 的堆栈帧
int foo()
{
int e = s; // 正确,s 是静态的
int f = j; // 错误,不能访问 test() 的堆栈帧
return c + a; // 正确,bar() 的堆栈帧是可访问的,
// 通过指向 Foo.bar() 的 this 指针
// 可以访问 Foo 的成员
}
return 0;
}
}
}
嵌套函数总是使用 D 函数链接类型。
同模块级的声明不同,函数作用域的声明会按照声明的顺序处理。这意味着连个嵌套函数不
能互相调用:
void test()
{
void foo() { bar(); } // 错误,bar 没有被定义
void bar() { foo(); } // 正确
}
解决的方法是使用委托:
void test()
{
void delegate() fp;
void foo() { fp(); }
void bar() { foo(); }
fp = &bar;
}
未来的方向 这个限制可能会被删除。
7.1 [color=red]委托、函数指针和动态闭包[/color]
[color=red]Delegates, Function Pointers, and Closures(2.014,不动态了?)[/color]
函数指针可以指向一个静态嵌套函数:
int function() fp;
void test()
{ static int a = 7;
static int foo() { return a + 3; }
fp = &foo;
}
void bar()
{
test();
int i = fp(); // i 被设置为 10
}
委托可以用非静态嵌套函数赋值:
int delegate() dg;
void test()
{ int a = 7;
int foo() { return a + 3; }
dg = &foo;
int i = dg(); // i 被设置为 10
}
[color=blue]但是,一旦声明堆栈变量的函数退出了,堆栈变量就不再有效;同样的,
指向堆栈变量的指针也不再有效:
int* bar()
{ int b;
test();
int i = dg(); // 错误,test.a 不再存在
return &b; // 错误,bar.b 在 bar() 退出后不再有效
}[/color]
[color=red]上面这段被改成:2.014
The stack variables referenced by a nested function are still valid even after the function exits (this is different from D 1.0). This is called a closure. Returning addresses of stack variables, however, is not a closure and is an error.
int* bar()
{ int b;
test();
int i = dg(); // ok, test.a is in a closure and still exists
return &b; // error, bar.b not valid after bar() exits
}[/color]
非静态嵌套函数的委托包括两块数据:指向外围函数堆栈帧的指针(叫做 帧指针)和函数
的地址。与此类似,结构/类的非静态成员函数委托由 this 指针和成员函数的地址组成。这
两种形式的委托可以互相转换,实际上它们具有相同的类型:
struct Foo
{ int a = 7;
int bar() { return a; }
}
int foo(int delegate() dg)
{
return dg() + 1;
}
void test()
{
int x = 27;
int abc() { return x; }
Foo f;
int i;
i = foo(&abc); // i 被设置为 28
i = foo(&f.bar); // i 被设置为 8
}
环境和函数的结合被称作 动态闭包。
委托的 .ptr 特性会返回 帧指针 值,而类型为 void*。
委托的 .funcptr 属性会返回 帧指针 值,类型为函数类型。
未来的方向 函数指针和委托或能被合并成一种通用的语法并且彼此可以互相更改。
7.2 [color=red]匿名函数和匿名委托[/color]
参看 函数字法。(参见 操作符与表达式 3 --> 基本表达式 21.10 函数字法)
8 main() 函数
对于控制台程序,main() 提供入口点。它会在所有的模块初始化,以及所有的单元测试都
被运行以后被调用。在它返回以后,所有的模块析构函数会得到运行。main() 必须使用下
列形式之一进行声明:
void main() { ... }
void main(char[][] args) { ... }
int main() { ... }
int main(char[][] args) { ... }
9 编译时 函数执行
有些函数的子集可以在编译时执行。这个在常量合拢(constant folding)需要包括递归
(recursion)和循环时很有用。为了在编译时可以被执行,此函数必须符号下面的标准
(criteria):
1. 函数形参都必须是:
• 整数文字
• 浮点文字
• 字符文字
• 字符串
• 数组文字,即所有成员都是这个列表的条目
• 关联数组文字,即所有成员都是这个列表的条目
• 结构文字,即所有成员都是这个列表的条目
• const 变量使用这个列表的一个成员进行初始化
2. 函数参数不能是参数可变型或者是 lazy 型
3. 函数不能被嵌套或同步(synchronized)
4. 函数不能是非静态(non-static)成员,即它不能有 this 指针
5. 在函数里的表达式不能:
• 抛出异常
• 使用指针、委托、非常量数组或者类
• 引用任何全局状态(state)或变量
• 引用任何局部变量
• 进行 new 或 delete 操作
• 调用任何在编译时不可执行的函数
6. 不允许下列语句类型:
• synchronized 语句
• throw 语句
• with 语句
• scope 语句
• try-catch-finally 语句
• 带标号的 break 和 continue 语句
7. 作为特殊情况,下列特性可以在编译时执行:
.dup
.length
.keys
.values
为了在编译时执行,函数必须出现在它必须那样执行的环境里,
比如:
• 静态变量的初始化
• 静态数组的维
• 用于模板值参(value parameter)的形式参数
template eval( A... )
{
const typeof(A[0]) eval = A[0];
}
int square(int i) { return i * i; }
void foo()
{
static j = square(3); // 编译时
writefln(j);
writefln(square(4)); // 运行时
writefln(eval!(square(5))); // 编译时
}
在编译时执行函数会比在运行时的执行花费更长的时间。如果函数进行无限循环状态,那么
它就会在编译时挂起(hang)(而不会在运行时挂起)。
在编译时执行的函数在下列场景下会给出跟运行时不同的结果:
• 浮点计算可能会以比运行时更高的精度进行
• 依赖于实现所定义的求值顺序
• 使用未初始化的变量
这些场景都跟不同优化设置影响会影响结果那样的场景一样。
9.1 字符串 混入和编译时的 函数执行
所有在编译时执行的函数都必须是在运行时可执行的。一个函数的编译时求值所完成的是跟
在运行时运行函数一样的。即表示函数的语义不能依赖于函数的编译时得到的值。
例如:
int foo(char[] s)
{
return mixin(s);
}
const int x = foo("1");
它就是非法的,因为不能为 foo() 生成运行时代码。函数模板用于实现此类事情会是种比较
合适的方法。
[/size]
.........