知识若不分享 实在没有意义 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)
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:
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:
TemplateInstance:
TemplateIdentifer !( TemplateArgumentList )
TemplateArgumentList:
TemplateArgument
TemplateArgument , TemplateArgumentList
TemplateArgument:
Type
AssignExpression
Symbol
一旦被实例化,位于模板内的声明,称做模板成员,就位于 TemplateInstance 作用域内:( 本文出处: http://www.d-programming-language-china.org )
template TFoo(T) { alias T* t; }
...
TFoo!(int).t x; // 声明 x 为 int* 类型
模板实例化可以有别名:
template TFoo(T) { alias T* t; }
alias TFoo!(int) abc;
abc.t x; // 声明 x 为 int* 类型
一个 模板声明 拥有相同 模板参数列表 的多个实例,在隐式转换前,将引用相同的实例。例如:
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:
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 )
例如,一个简单的泛型复制函数可以是这个样子:
template TCopy(T)
{
void copy(out T to, T from)
{
to = from;
}
}
在使用模板之前,必须先使用具体的类型将其实例化:
int i;
TCopy!(int).copy(i, 3);
Instantiation Scope实例化作用域
模板实例 总是在声明 模板声明 的作用域内执行,另外声明的模板参数被看作它们所推出的类型的别名。
例如:
-------- module a ---------
template TFoo(T) { void bar() { func(); } }
-------- module b ---------
import a;
void func() { }
alias TFoo!(int) f; // 错误:func 没有在模块 a 内定义
以及:
-------- module a ---------
template TFoo(T) { void bar() { func(1); } }
void func(double d) { }
-------- module b ---------
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)的方法,模板实例中的模板参数的类型被推倒出来。
对于每个模板参数,按照下面的顺序逐条应用规则直到每个参数的类型都被推倒出来:
如果参数没有指定一个特化,参数的类型被设为指定的模板实参。
如果类型特化依赖于一个类型参数,这个参数的类型就被设为与那个类型实参对应的类型。
如果在检查了所有类型实参之后还有类型参数没有被分配类型,它们就会被分配给在 模板参数列表 中位于相同位置的模板实参。
如果应用上述规则之后,还不能做到每个模板参数都精确的对应唯一一个类型,那么就被视为错误。
例如:
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 )
template Foo(T: T[U], U)
{
...
}
Foo!(int[long]) // instantiates Foo with T set to int, U set to long
当考虑匹配时,一个类被认为可以匹配任何父类或接口:
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 的值参数:
template foo(U : int, int T : 10)
{
U x = T;
}
void main()
{
assert(foo!(int, 10).x == 10);
}
Specialization特化
模板可以通过在模板参数之后指定一个“:”和一个特化类型来将模板特化为使用某些指定的实参类型。例如:
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 )
全局名
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
}
类型名
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
}
模块名
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)
}
模板名
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
}
模板别名
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模板参数默认值
右端的模板参数(从最右端的那个开始向左连续的带有默认值的那些参数)可以有默认值:
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隐式模板属性
如果模板有且只有一个成员,并且这个成员和模板同名的话,这个成员就被认为引用的是一个模板实例:
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 )
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:
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:
The tuple can also be deduced from the type of a delegate or function parameter list passed as a function argument:
/* 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类模板
类模板声明:
class 标志符( 模板参数列表) [父类{, 接口类}] 类过程体
ClassTemplateDeclaration:
class Identifier ( TemplateParameterList ) [SuperClass {, InterfaceClass }] ClassBody
如果一个模板声明且仅声明了一个成员,并且那个成员是一个同模板同名的类:
template Bar(T)
{
class Bar
{
T member;
}
}
则同下面的声明语义等价,称作 类模板声明 :
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:
FunctionTemplateDeclaration:
类型标识符 (模版参数列表 ) ( 函数参数列表 ) 函数体
Type Identifier ( TemplateParameterList ) ( FunctionParameterList ) FunctionBody
一个计算类型为T的平方的函数为:( 本文出处: http://www.d-programming-language-china.org )
T Square(T)(T t)
{
return t * t;
}
Function templates can be explicitly instantiated with a !(TemplateArgumentList):
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:
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:
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:
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 )
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限制
模板不能用来给类添加非静态成员或函数。例如:
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为大家提供一个学习交流各种知识的平台