yidabu 2007-4-26 15:59
19 D语言 模板 Templates
19 D语言 模板 Templates知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070424点击下面网址查看原文:http://www.d-programming-language-china.org by: uFramer D语言论坛 http://www.d-programming-language-china.org from: http://www.digitalmars.com/d/template.html version: 基于D 1.013 (Apr 19, 2007) QUOTE: I think that I can safely say that nobody understands template mechanics. -- Richard Deyman Templates are D's approach to generic programming. Templates are defined with a TemplateDeclaration: QUOTE: TemplateDeclaration: template TemplateIdentifier ( TemplateParameterList ) { DeclDefs } TemplateIdentifier: Identifier TemplateParameterList TemplateParameter TemplateParameter , TemplateParameterList TemplateParameter: TemplateTypeParameter TemplateValueParameter TemplateAliasParameter TemplateTupleParameter TemplateTypeParameter: Identifier Identifier TemplateTypeParameterSpecialization Identifier TemplateTypeParameterDefault Identifier TemplateTypeParameterSpecialization TemplateTypeParameterDefault TemplateTypeParameterSpecialization: : Type TemplateTypeParameterDefault: = Type TemplateValueParameter: Declaration Declaration TemplateValueParameterSpecialization Declaration TemplateValueParameterDefault Declaration TemplateValueParameterSpecialization TemplateValueParameterDefault TemplateValueParameterSpecialization: : ConditionalExpression TemplateValueParameterDefault: = ConditionalExpression TemplateAliasParameter: alias Identifier alias Identifier TemplateAliasParameterSpecialization alias Identifier TemplateAliasParameterDefault alias Identifier TemplateAliasParameterSpecialization TemplateAliasParameterDefault TemplateAliasParameterSpecialization: : Type TemplateAliasParameterDefault: = Type TemplateTupleParameter: Identifier ... 无论模板是否被最终实例化,模板声明 的过程体在语法上必须是正确的。语义分析延迟到模板实例化时进行。模板有自己的作用域,模板过程体中可以包括类、结构、类型、枚举、变量、函数或者其它模板。 模板参数可以是类型、值或者符号,或者tuple。可以使用任何类型。值参数必须是整数型,浮点指针型,字符串型,在特化时它们必须被解析为整数常量,浮点指针常量,null或者字符串文字量。符号可以是任何非局部符号。 Tuples are a sequence of 0 or more types, values or symbols. 模板参数特化将值或类型约束为 模板参数 可以接受的值或类型。 模板参数默认值用在不提供 模板参数 时。Explicit Template Instantiation Templates are explicitly instantiated with: QUOTE: TemplateInstance: TemplateIdentifer !( TemplateArgumentList ) TemplateArgumentList: TemplateArgument TemplateArgument , TemplateArgumentList TemplateArgument: Type AssignExpression Symbol 一旦被实例化,位于模板内的声明,称做模板成员,就位于 TemplateInstance 作用域内:( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: template TFoo(T) { alias T* t; } ... TFoo!(int).t x; // 声明 x 为 int* 类型 模板实例化可以有别名: [Copy to clipboard] [ - ]CODE: template TFoo(T) { alias T* t; } alias TFoo!(int) abc; abc.t x; // 声明 x 为 int* 类型 一个 模板声明 拥有相同 模板参数列表 的多个实例,在隐式转换前,将引用相同的实例。例如: [Copy to clipboard] [ - ]CODE: template TFoo(T) { T f; } alias TFoo(int) a; alias TFoo(int) b; ... a.f = 3; assert(b.f == 3); // a 和 b 引用相同的 TFoo 实例 即使 模板实例 位于不同的模块中,这条规则也成立。 Even if template arguments are implicitly converted to the same template parameter type, they still refer to different instances: [Copy to clipboard] [ - ]CODE: struct TFoo(int x) { } static assert(is(TFoo!(3) == TFoo!(2 + 1))); // 3 and 2+1 are both 3 of type int static assert(!is(TFoo!(3) == TFoo!(3u))); // 3u and 3 are different types 如果声明了拥有多个相同 模板标志符 的模板,并且它们参数个数不同或者采用不同的特化,那么它们不同。( 本文出处: http://www.d-programming-language-china.org ) 例如,一个简单的泛型复制函数可以是这个样子: [Copy to clipboard] [ - ]CODE: template TCopy(T) { void copy(out T to, T from) { to = from; } } 在使用模板之前,必须先使用具体的类型将其实例化: [Copy to clipboard] [ - ]CODE: int i; TCopy!(int).copy(i, 3); Instantiation Scope实例化作用域 模板实例 总是在声明 模板声明 的作用域内执行,另外声明的模板参数被看作它们所推出的类型的别名。 例如: -------- module a --------- [Copy to clipboard] [ - ]CODE: template TFoo(T) { void bar() { func(); } } -------- module b --------- [Copy to clipboard] [ - ]CODE: import a; void func() { } alias TFoo!(int) f; // 错误:func 没有在模块 a 内定义 以及: -------- module a --------- [Copy to clipboard] [ - ]CODE: template TFoo(T) { void bar() { func(1); } } void func(double d) { } -------- module b --------- [Copy to clipboard] [ - ]CODE: import a; void func(int i) { } alias TFoo!(int) f; ... f.bar(); // 将调用 a.func(double) 模板参数 的特化和默认值将在 模板声明 的作用域内估值。( 本文出处: http://www.d-programming-language-china.org )Argument Deduction参数推导 采用比较对应的模板参数(template parameter)和模板实参(template argument)的方法,模板实例中的模板参数的类型被推倒出来。 对于每个模板参数,按照下面的顺序逐条应用规则直到每个参数的类型都被推倒出来: 如果参数没有指定一个特化,参数的类型被设为指定的模板实参。 如果类型特化依赖于一个类型参数,这个参数的类型就被设为与那个类型实参对应的类型。 如果在检查了所有类型实参之后还有类型参数没有被分配类型,它们就会被分配给在 模板参数列表 中位于相同位置的模板实参。 如果应用上述规则之后,还不能做到每个模板参数都精确的对应唯一一个类型,那么就被视为错误。 例如: [Copy to clipboard] [ - ]CODE: template TFoo(T) { } alias TFoo!(int) Foo1; // (1) T is deduced to be int alias TFoo!(char*) Foo2; // (1) T is deduced to be char* template TBar(T : T*) { } alias TBar!(char*) Foo3; // (2) T is deduced to be char template TAbc(D, U : D[]) { } alias TAbc!(int, int[]) Bar1; // (2) D is deduced to be int, U is int[] alias TAbc!(char, int[]) Bar2; // (4) error, D is both char and int template TDef(D : E*, E) { } alias TDef!(int*, int) Bar3; // (1) E is int // (3) D is int* Deduction from a specialization can provide values for more than one parameter:( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: template Foo(T: T[U], U) { ... } Foo!(int[long]) // instantiates Foo with T set to int, U set to long 当考虑匹配时,一个类被认为可以匹配任何父类或接口: [Copy to clipboard] [ - ]CODE: class A { } class B : A { } template TFoo(T : A) { } alias TFoo!(B) Foo4; // (3) T is B template TBar(T : U*, U : A) { } alias TBar!(B*, B) Foo5; // (2) T is B* // (3) U is B Value Parameters值参数 这个模板例子中,指定了一个为 10 的值参数: [Copy to clipboard] [ - ]CODE: template foo(U : int, int T : 10) { U x = T; } void main() { assert(foo!(int, 10).x == 10); } Specialization特化 模板可以通过在模板参数之后指定一个“:”和一个特化类型来将模板特化为使用某些指定的实参类型。例如: [Copy to clipboard] [ - ]CODE: template TFoo(T) { ... } // #1 template TFoo(T : T[]) { ... } // #2 template TFoo(T : char) { ... } // #3 template TFoo(T,U,V) { ... } // #4 alias TFoo!(int) foo1; // 实例化 #1 alias TFoo!(double[]) foo2; // 实例化 #2 ,其中 T 为 double alias TFoo!(char) foo3; // 实例化 #3 alias TFoo!(char, int) fooe; // 错误,实参个数不匹配 alias TFoo!(char, int, int) foo4; // 实例化 #4 当进行模板实例化时,会挑选匹配 模板参数列表 的特化度最高的模板。决定那个模板更为特化的方式同 C++ 处理偏序规则的方式相同。如果结果是模棱两可的,就是错误。Alias Parameters别名参数 别名参数使模板能够使用任何 D 符号参数化,包括全局名称、类型名称、模板名称以及模板实例名称。这是 C++ 中将模板作为模板参数的做法的超集。( 本文出处: http://www.d-programming-language-china.org ) 全局名 [Copy to clipboard] [ - ]CODE: int x; template Foo(alias X) { static int* p = &X; } void test() { alias Foo!(x) bar; *bar.p = 3; // set x to 3 static int y; alias Foo!(y) abc; *abc.p = 3; // set y to 3 } 类型名 [Copy to clipboard] [ - ]CODE: class Foo { static int p; } template Bar(alias T) { alias T.p q; } void test() { alias Bar!(Foo) bar; bar.q = 3; // 将 Foo.p 设置为 3 } 模块名 [Copy to clipboard] [ - ]CODE: import std.string; template Foo(alias X) { alias X.toString y; } void test() { alias Foo!(std.string) bar; bar.y(3); // 调用 std.string.toString(3) } 模板名 [Copy to clipboard] [ - ]CODE: int x; template Foo(alias X) { static int* p = &X; } template Bar(alias T) { alias T!(x) abc; } void test() { alias Bar!(Foo) bar; *bar.abc.p = 3; // sets x to 3 } 模板别名 [Copy to clipboard] [ - ]CODE: int x; template Foo(alias X) { static int* p = &X; } template Bar(alias T) { alias T.p q; } void test() { alias Foo!(x) foo; alias Bar!(foo) bar; *bar.q = 3; // sets x to 3 } Template Parameter Default Values模板参数默认值 右端的模板参数(从最右端的那个开始向左连续的带有默认值的那些参数)可以有默认值: [Copy to clipboard] [ - ]CODE: template Foo(T, U = int) { ... } Foo!(uint,long); // 实例化:T 为 uint ,U 为 long Foo!(uint); // 实例化:T 为 uint ,U 为 int template Foo(T, U = T*) { ... } Foo!(uint); // 实例化:T 为 uint ,U 为 uint* Implicit Template Properties隐式模板属性 如果模板有且只有一个成员,并且这个成员和模板同名的话,这个成员就被认为引用的是一个模板实例: [Copy to clipboard] [ - ]CODE: template Foo(T) { T Foo; // 声明变量 Foo 为类型 T } void test() { Foo!(int) = 6; // 代替 Foo!(int).Foo } Tuple Parameters If the last template parameter in the TemplateParameterList is declared as a TemplateTupleParameter, it is a match with any trailing template arguments. The sequence of arguments form a Tuple. A Tuple is not a type, an expression, or a symbol. It is a sequence of any mix of types, expressions or symbols. A Tuple whose elements consist entirely of types is called a TypeTuple. A Tuple whose elements consist entirely of expressions is called an ExpressionTuple. A Tuple can be used as an argument list to instantiate another template, or as the list of parameters for a function.( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: template Print(A ...) { void print() { writefln("args are ", A); } } template Write(A ...) { void write(A a) // A is a TypeTuple // a is an ExpressionTuple { writefln("args are ", a); } } void main() { Print!(1,'a',6.8).print(); // prints: args are 1a6.8 Write!(int, char, double).write(1, 'a', 6.8); // prints: args are 1a6.8 } Template tuples can be deduced from the types of the trailing parameters of an implicitly instantiated function template: [Copy to clipboard] [ - ]CODE: template Foo(T, R...) { void Foo(T t, R r) { writefln(t); static if (r.length) // if more arguments Foo(r); // do the rest of the arguments } } void main() { Foo(1, 'a', 6.8); } prints: QUOTE: 1 a 6.8 The tuple can also be deduced from the type of a delegate or function parameter list passed as a function argument: [Copy to clipboard] [ - ]CODE: /* R is return type * A is first argument type * U is TypeTuple of rest of argument types */ R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) { struct Foo { typeof(dg) dg_m; typeof(arg) arg_m; R bar(U u) { return dg_m(arg_m, u); } } Foo* f = new Foo; f.dg_m = dg; f.arg_m = arg; return &f.bar; } void main() { int plus(int x, int y, int z) { return x + y + z; } auto plus_two = Curry(&plus, 2); printf("%d\n", plus_two(6, 8)); // prints 16 } The number of elements in a Tuple can be retrieved with the .length property. The nth element can be retrieved by indexing the Tuple with [n], and sub tuples can be created with the slicing syntax. Tuples are static compile time entities, there is no way to dynamically change, add, or remove elements.( 本文出处: http://www.d-programming-language-china.org ) If both a template with a tuple parameter and a template without a tuple parameter exactly match a template instantiation, the template without a TemplateTupleParameter is selected.Class Templates类模板 QUOTE: 类模板声明: class 标志符( 模板参数列表) [父类{, 接口类}] 类过程体 ClassTemplateDeclaration: class Identifier ( TemplateParameterList ) [SuperClass {, InterfaceClass }] ClassBody 如果一个模板声明且仅声明了一个成员,并且那个成员是一个同模板同名的类: [Copy to clipboard] [ - ]CODE: template Bar(T) { class Bar { T member; } } 则同下面的声明语义等价,称作 类模板声明 : [Copy to clipboard] [ - ]CODE: class Bar(T) { T member; } Function Templates函数模版 If a template declares exactly one member, and that member is a function with the same name as the template: QUOTE: FunctionTemplateDeclaration: 类型标识符 (模版参数列表 ) ( 函数参数列表 ) 函数体 Type Identifier ( TemplateParameterList ) ( FunctionParameterList ) FunctionBody 一个计算类型为T的平方的函数为:( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: T Square(T)(T t) { return t * t; } Function templates can be explicitly instantiated with a !(TemplateArgumentList): [Copy to clipboard] [ - ]CODE: writefln("The square of %s is %s", 3, Square!(int)(3)); or implicitly, where the TemplateArgumentList is deduced from the types of the function arguments: [Copy to clipboard] [ - ]CODE: writefln("The square of %s is %s", 3, Square(3)); // T is deduced to be int Function template type parameters that are to be implicitly deduced may not have specializations: [Copy to clipboard] [ - ]CODE: void Foo(T : T*)(T t) { ... } int x,y; Foo!(int*)(x); // ok, T is not deduced from function argument Foo(&y); // error, T has specialization Template arguments not implicitly deduced can have default values: [Copy to clipboard] [ - ]CODE: void Foo(T, U=T*)(T t) { U p; ... } int x; Foo(&x); // T is int, U is int* Recursive Templates递归模板 可以组合模板的各种特性来产生一些有趣的效果,例如在编译时对非平凡函数求值。例如,可以写一个计算阶乘的模板:( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: template factorial(int n : 1) { enum { factorial = 1 } } template factorial(int n) { enum { factorial = n* factorial!(n-1) } } void test() { writefln("%s", factorial!(4)); // prints 24 } Limitations限制 模板不能用来给类添加非静态成员或函数。例如: [Copy to clipboard] [ - ]CODE: class Foo { template TBar(T) { T xx; // 错误 int func(T) { ... } // 错误 static T yy; // Ok static int func(T t, int y) { ... } // Ok } } 不能在函数内部声明模板。( lastupdate:20070426 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:yidabu提倡在交流中学习,在分享中提高收集感兴趣的知识,写下心得,通过网络与别人一起分享理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台