发新话题
打印

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 )

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:

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:

/* 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

如果一个模板声明且仅声明了一个成员,并且那个成员是一个同模板同名的类:

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:

QUOTE:
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为大家提供一个学习交流各种知识的平台

TOP

发新话题