`
hqs7636
  • 浏览: 221000 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

模 板 2.014

阅读更多
我想我可以担保地说没什么人理解了模板机制。 -- Richard Deyman

模板(Templates)是 D 实现泛型编程(generic programming)的方法。模板通过 模板声明 进行定义:

模板声明:
template 模板标识符 ( 模板参数列表 )
{ 多个声明定义 }

模板标识符:
标识符
模板参数列表
模板参数
模板参数 , 模板参数列表

模板参数:
模板类型参数
模板值参数
模板别名参数
模板元组参数
TemplateThisParameter  (2.014)


无论模板是否被最终实例化,模板声明 的过程体 在语法上必须是正确的。语义分析延迟到
模板实例化 时进行。

模板有自己的作用域,模板过程体中可以 包括类、结构、类型、枚举、变量、函数或者其它模板。

模板参数可以是类型、具体值、符号或者元组(Tuple)。其类型可以使用任何类型。

值参数必须是整数型,在特化时它们必须被解析为整数常量。符号可以是任何 非局部符号。

元组(Tuple)是一种由 0 个或更多的 类型、值或 符号 组成的序列。

模板参数特化 将值或类型约束为 模板参数 可以接受的 值或类型。

模板参数默认值 用在不提供 模板参数 时。


17.1 显示模板 实例化(Template Instantiation)

模板显示实例化的方式:

模板实例:
模板标识符 !(模板参数列表 )

模板参数列表:
模板参数
模板参数 , 模板参数列表

模板参数:
类型
赋值表达式
符号

一旦被实例化,则位于该模板内的声明,也叫做模板成员(template members),就处于该 模板实例的 作用域内:

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;         (!(int)  ??)
alias TFoo!(int) b;
...
a.f = 3;
assert(b.f == 3); // a 和 b 引用相同的 TFoo 实例

即使 模板实例 完成于不同的模块中,这条规则也成立。

即使模板 实参 会被 隐式转换 成相同的模板 形参 类型,它们仍然会引用到不同的实例:
struct TFoo(int x) { }
static assert(is(TFoo!(3) == TFoo!(2 + 1))); // 3 和 2+1 都是类型为 int 的 3
static assert(!is(TFoo!(3) == TFoo!(3u))); // 3u 和 3 是不同的类型

如果声明了拥有多个相同 模板标识符 的模板,并且它们参数个数不同或者采用不同的特
化,那么它们不同。

例如,一个简单的泛型复制函数可以是这个样子:
template TCopy(T)
{
void copy(out T to, T from)
{
to = from;
}
}

在使用模板之前,必须先使用具体的类型将其实例化:
int i;
TCopy!(int).copy(i, 3);


2 实例化作用域

模板实例 总是在声明 板声明 的作用域内执行,另外声明的模板参数被看作它们所推出的
类型的别名。

例如:
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)

模板参数 的特化和默认值将在 模板声明 的作用域内被求值。


3 参数推导

采用比较对应的模板形参(template parameter)和模板实参(template argument)的方法,模板实例中的模板参数的类型被推倒出来。

对于每个模板参数,按照下面的顺序 逐条应用规则 直到每个参数的类型都被 推倒 出来:
1. 如果参数没有指定一个 特化,参数的类型被设为指定的模板 实参。
2. 如果类型特化依赖于一个类型参数,这个参数的类型就被设为与那个类型实参对应的
类型。
3. 如果在检查了所有类型实参之后还有类型参数没有被分配类型,它们就会被分配给在
模板参数列表 中位于相同位置的模板实参。
4. 如果应用上述规则之后,还不能做到每个模板参数都精确的对应唯一一个类型,那么
就被视为错误。

例如:

template TFoo(T) { }
alias TFoo!(int) Foo1; // (1) T 被推导为 int
alias TFoo!(char*) Foo2; // (1) T 被推导为 char*
template TBar(T : T*) { }
alias TBar!(char*) Foo3; // (2) T 被推导为 char
template TAbc(D, U : D[]) { }
alias TAbc!(int, int[]) Bar1; // (2) D 被推导为 int, U 被推导为 int[]
alias TAbc!(char, int[]) Bar2; // (4) 错误,D 既是 char 又是 int
template TDef(D : E*, E) { }
alias TDef!(int*, int) Bar3; // (1) E 是 int
// (3) D 是 int*

从一个特例化进行的推演可以为多个参数提供值:
template Foo(T: T[U], U)
{
...
}
Foo!(int[long]) // 实例化 Foo:将 T 设置成 int,而 U 设置成 long

当考虑匹配时,一个类被认为可以匹配任何父类或接口:
class A { }
class B : A { }
template TFoo(T : A) { }
alias TFoo!(B) Foo4; // (3) T 是 B
template TBar(T : U*, U : A) { }
alias TBar!(B*, B) Foo5; // (2) T 是 B*
// (3) U 是 B


4 模板类型参数(Template Type Parameters)

模板类型参数:
标识符
标识符 模板类型参数特化
标识符 模板类型参数默认值
标识符 模板类型参数特化 模板类型参数默认值

模板类型参数特化:
: 类型

模板类型参数默认值:
= 类型


4.1 特例化(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++ 处理偏序规则的方式相同。如果结果是模棱两可的,就是错误。


2.014
Template This Parameters

TemplateThisParameter:
this TemplateTypeParameter
TemplateThisParameters are used in member function templates to pick up the type of the this reference.

import std.stdio;

struct S
{
    const void foo(this T)(int i)
    {
writeln(typeid(T));
    }
}

void main()
{
    const(S) s;
    (&s).foo(1);
    S s2;
    s2.foo(2);
    invariant(S) s3;
    s3.foo(3);
}
Prints:

const(S)
S
invariant(S)



5 模板值参数(Template Value Parameters)

模板值参数:
单个声明
单个声明 模板值参数特化
单个声明 模板值参默认值
单个声明 模板值参数特化 模板值参默认值

模板值参数特化:
: 条件表达式

模板值参默认值:
2.014
= __FILE__
= __LINE__

= 条件表达式

2.014
The __FILE__ and __LINE__ expand to the source file name and line number at the point of instantiation.


这个模板例子中,指定了一个为 10 的值参数:
template foo(U : int, int T : 10)
{
U x = T;
}
void main()
{
assert(foo!(int, 10).x == 10);
}


17.6 模板别名参数(Template Alias Parameters)

模板别名参数:
alias 标识符
alias 标识符 模板别名参数特例化
alias 标识符 模板别名参数默认值
alias 标识符 模板别名参数特例化 模板别名参数默认值

模板别名参数特例化:
: 类型

模板别名参数默认值:
= 类型

别名参数使模板能够使用任何 D 符号参数化,包括全局名称、类型名称、模板名称以及模
板实例名称。

局部名字可能不能作为别名参数。这是 C++ 中将模板作为模板参数的做法的
超集。

• 全局名

int x;
template Foo(alias X)
{
static int* p = &X;
}
void test()
{
alias Foo!(x) bar;
*bar.p = 3; // 把 x 设置为 3
static int y;
alias Foo!(y) abc;
*abc.p = 3; // 把 y 设置为 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; // 把 x 设置为 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; // 把 x 设置为 3
}


7 模板元组参数(Template Tuple Parameters)

模板元组参数:
标识符 ...

如果在 模板参数列表 里的最后一个模板参数被声明为 模板元组参数,那么它会跟任意的
模板尾部参数进行匹配。参数的序列组成了一个 元组(Tuple)。一个 元组(Tuple) 不是一种类型、表达式或者符号。它是一种由类型、表达式或符号的任意混合的序列。

一个它的元素完全由类型组成的 元组(Tuple) 叫做 类型元组(TypeTuple)。一个它的元素完全由表达式组成的 元组(Tuple) 叫做 表达式元组(ExpressionTuple)。

一个 元组(Tuple) 可被用做参数列表并实例化别一个模板,或者被用作一个函数的参数列
表。

template Print(A ...)
{
void print()
{
writefln("args are ", A);
}
}
template Write(A ...)
{
void write(A a) // A 是一个 类型元组
// a 是一个 表达式元组
{
writefln("args are ", a);
}
}
void main()
{
Print!(1,'a',6.8).print(); // 输出结果:args 是 1a6.8
Write!(int, char, double).write(1, 'a', 6.8); // prints:args 是 1a6.8
}

模板元组(template tuples)可以由一个隐式实例化的函数模板的尾部参数的类型进行推导:

template Foo(T, R...)
{
void Foo(T t, R r)
{
writefln(t);
static if (r.length) // 如果有更多的实参
Foo(r); // 处理其余的实参
}
}
void main()
{
Foo(1, 'a', 6.8);
}

输出:
1a
6.8

Tuple 可以由一个委托或函数的传递为函数形参的参数列表的类型进行推导:

import std.stdio;
/* R 是 return 类型
* A 是第一个实参类型
* U 是剩余形参类型的 类型元组
*/
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);
writefln("%d", plus_two(6,); // 输出 16
}

在一个 元组(Tuple) 里的元素数目可以使用 .length 特性获得。第 n 个元素可以通过使用 [n] 来索引该 元组(Tuple) 得到,而对于子元组(sub tuples)则可以通过分割语法(slicing syntax) 来创建。

元组(Tuple) 是静态的编译时实体,因此没有办法来动态更改、添加或删除元素。

如果一个带有元组(Tuple)参数的模板跟一个不带元组(Tuple)参数的模板都完全匹配于某一个模板实例,则不带有 模板元组参数 的模板会选择。


8 模板参数默认值

尾端(最右边)的模板参数可以被给定默认值:

template Foo(T, U = int) { ... }
Foo!(uint,long); // 实例化 Foo:T 替换为 uint ,U 替换为 long
Foo!(uint); // 实例化 Foo:T 替换为 uint ,U 替换为 int
template Foo(T, U = T*) { ... }
Foo!(uint); // 实例化 Foo:T 替换为 uint ,U 替换为 uint*


9 隐式模板特性

如果模板有且只有一个成员,并且这个成员和模板同名的话,这个成员就被认为引用的是一
个模板实例:

template Foo(T)
{
T Foo; // 声明变量 Foo 为类型 T
}
void test()
{
Foo!(int) = 6; // 代替 Foo!(int).Foo
}


10 类模板(Class Templates)

类模板声明:
class 标识符 ( 模板参数列表 ) [父类 {, 接口类 }] 类过程体如果一个模板声明且仅声明了一个成员,并且那个成员是一个同模板同名的类:

template Bar(T)
{
class Bar
{
T member;
}
}

则同下面的声明语义等价,称作 类模板声明:

class Bar(T)
{
T member;
}


11 结构、联合以及接口模板(Struct, Union, and Interface Templates)

跟类模板类似,结构(struct)、联合(union)以及接口(interface)都可以通过提供一个参数列表来传递进模板。


12 函数模板(Function Templates)

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

函数模板声明:
类型 标识符 ( 模板参数列表 ) ( 函数参数列表 ) 函数体
一个用于计算类型为 T 的平方的函数模板就是:

T Square(T)(T t)
{
return t * t;
}

函数模板可以通过使用 !(TemplateArgumentList) 进行显式的实例化:
writefln("The square of %s is %s", 3, Square!(int)(3));

或者进行隐式实例化,而此时的 模板参数列表 则由函数形参的类型进行 推导:

writefln("The square of %s is %s", 3, Square(3)); // T 被推导为 int

那些要被隐式推导的函数模板类型参数不可能进行特例化:

void Foo(T : T*)(T t) { ... }
int x,y;
Foo!(int*)(x); // 正确,T 不是推导自函数形参
Foo(&y); // 错误,T 进行了特例化

不能被隐式推导的模板形参可以有默认值:

void Foo(T, U=T*)(T t) { U p; ... }
int x;
Foo(&x); // T is int, U is int*


13 递归模板(Recursive Templates)

可以组合模板的各种特性来产生一些有趣的效果,例如在编译时对非平凡函数求值。例如,
可以写一个计算阶乘的模板:

template factorial(int n : 1)
{
enum { factorial = 1 }
}
template factorial(int n)
{
enum { factorial = n* factorial!(n-1) }
}
void test()
{
writefln("%s", factorial!(4)); // 输出 24
}


14 限制(Limitations)

模板不能用来给类添加非静态成员或函数。

例如:
class Foo
{
template TBar(T)
{
T xx; // 错误
int func(T) { ...} // 错误
static T yy; // 正确
static int func(T t, int y) { ...} // 正确
}
}


不能在函数内部声明模板。


。。。。。。。。。。
分享到:
评论

相关推荐

    手绘水彩PPT模板-014.zip

    2. **页面布局**:模板通常会提供多种预设的页面布局,如标题页、内容页、图表页、图片展示页等,方便用户根据需要选择和编辑。 3. **动画与过渡效果**:水彩风格的动画和过渡效果可以增强演示的流畅性,使观众更加...

    PPT模板-014.清新蓝色简洁设计PPT.rar

    2. **色彩**:使用有限的色板,通常不超过3-4种颜色,以保持视觉一致性。 3. **字体**:选择易读的字体,大小适中,确保在远距离也能清晰看到文本。 4. **空白空间**:合理运用留白,增强视觉效果,让内容更突出。...

    医学护理PPT模板-014.pptx

    2. **标题设计**:每个页面都有预留的“ADD TITLE”区域,用户可以自定义添加与主题相关的标题,如疾病介绍、护理程序、治疗方案等,这有助于引导观众关注关键信息。 3. **内容区域**:“You can click here to ...

    建筑施工组织2021-014曲面构件模板施工.doc

    2. 桁架搭建:使用钢材或其他高强度材料,按照设计要求搭建可变桁架结构。这个过程需要考虑到模板的支撑稳定性以及拆卸的便利性。 3. 模板安装:在桁架上固定曲面模板,模板材料通常选用木质或铝合金,以确保其足够...

    [014]高端商务PPT模板[淡雅唯美工作汇报总结模板].zip

    2. 目录页:列出报告的主要章节,帮助观众快速了解内容结构。 3. 引言部分:简述报告的目的、背景和重要性,为后续内容铺垫。 4. 数据分析页:使用图表、图形等可视化工具展示数据,便于理解复杂的业务指标和趋势...

    TEM014 - 新版阿里云网站界面高保真原型模板-AxureUX.7z

    2. **用户体验设计**:设计时需考虑用户操作流程的便利性和逻辑性,确保每个功能易于理解和使用,符合用户的使用习惯。 3. **响应式设计**:阿里云网站应具备良好的跨设备兼容性,因此原型设计应考虑到不同屏幕尺寸...

    极简北欧风格PPT模板素材-编号014.zip

    2. **北欧风格**:北欧设计注重实用性和美学的结合,常常体现为淡色调、木质元素、几何形状以及自然光线的运用。在PPT中,这可能体现在使用浅色背景(如白色或淡灰色)、简单的图形设计、自然纹理(如木质或布艺纹理...

    TEM014 - 新版阿里云网站界面高保真原型模板.zip

    2. "用户中心-后台用户管理系统V1.4.rp":这是针对用户中心的后台管理系统原型,可能包含了用户注册、登录、信息管理、权限设置、账户安全等模块。通过这个模板,设计团队可以清晰地展示如何有效地管理和维护用户的...

    OR-014_企业网站网页PSD整站模板.zip

    2. **网站模板**: 网站模板是一种预先设计好的网页布局,包含了颜色方案、字体样式、图像布局等元素,可以作为快速构建网站的基础。企业网站模板通常是根据商业需求定制的,包括首页、关于我们、服务、产品、联系...

    【产品经理工具包-组件库】 - 新版阿里云网站界面高保真原型模板-AxureUX.zip

    2. **交互模拟**:Axure UX 支持设置动态面板、链接跳转、条件逻辑等,使得原型具备初步的交互效果,便于测试和验证用户流程。 3. **一致性**:使用统一的组件库,确保设计的一致性和专业性,符合阿里云的品牌规范。...

    AUTOCAD转换流程模板.doc

    2. MAPGIS里代码查找:打开“图形处理”下的“数字测图”子系统,选择【文件】菜单下的“新建”子菜单,弹出“新建”对话框;选择“测量工程文件”点击“确定”;弹出“另存为”对话框,选择路径、命名,点击“确定...

    014_html网站模板_网页源码移动端前端_H5模板_自适应响应式源码.zip

    2. CSS文件:用于定义页面样式和布局,可能包括一个或多个样式表。 3. JavaScript文件:包含实现动态功能和交互的代码,可能有独立的库文件和项目特定的脚本。 4. 图像资源:如背景图片、图标等,可能在img目录下。 ...

    RED-001_企业网站网页PSD整站模板.zip

    2. "bt014_002_01.psd"至"bt014_002_06.psd":这些是PSD文件,代表网站的不同页面或组件。例如,bt014可能是设计项目编号,002可能是设计版本,数字序列可能表示不同页面,如首页、产品页、关于我们页等。每个PSD...

    工作总结汇报通用 PPT模板(014).pptx

    2. **目标达成**:阐述你设定的目标以及如何实现这些目标,这可以包括完成的项目、达成的业绩指标、优化的工作流程等。 3. **关键成就**:突出你的主要成就,可能是新的业务拓展、成本节省、效率提升或是团队建设的...

    蓝色水晶企业CSS模板3354_html网站模板_网页源码移动端前端_H5模板_自适应响应式源码.rar

    2. CSS3特效与布局: CSS3提供了丰富的样式和动画功能,如阴影、渐变、旋转、过渡和动画等。此模板利用CSS3的Flexbox或Grid布局,实现响应式设计,确保页面在不同屏幕尺寸下都能保持良好的视觉效果。同时,CSS3还...

    酱油原始记录(表格模板、DOC格式).doc

    2. **可溶性无盐固形物**:这是衡量酱油营养成分的一个指标,包括蛋白质、糖类等。计算公式为 (m2-m1)X2 × 100 / 5,其中m2是包含样品的可溶性总固形物和称量瓶的质量,m1是称量瓶的质量。这个指标反映了酱油的浓度...

    某集团人事相关表格模板.doc

    2. **应聘人员登记表(CMHR--002)**:用于收集应聘者的个人信息、教育背景、工作经历等,还有健康状况和期望薪资,以评估其是否符合职位要求。应聘者需保证信息的真实性,否则可能面临解雇风险。 3. **面试评价表...

    主桥支架模板施工安全技术交底.docx

    该桥的跨度分布为10*20m+(30+53+30)+8*20m,其中主桥P10#墩-P13#墩的部分为变截面预应力钢筋混凝土连续箱梁,高度从6.014米至7.292米不等。箱梁设计为双箱室结构,通过横梁和桥面连接,顶底面有2%的横坡。箱梁的...

    四色扁平化大气欧美风商业计划书PPT模板.pptx

    2. **结构布局**:商业计划书PPT通常包括标题页、目录、公司介绍、产品/服务描述、市场分析、营销策略、财务预测、团队介绍和发展历程等部分。模板中的01到04页代表这些部分的标题和内容描述,应确保每个页面的内容...

    BL-014_psd网页模板_网站psd效果图切片_PSD欧美日韩风格前端页面.zip

    2. **效率提升**:使用现成的PSD模板可以节省设计师大量的时间,尤其是对于需要快速推出原型或测试不同设计风格的情况。 3. **切片与导出**:在"网站psd效果图切片"过程中,设计师会根据网页的各个部分进行切片,...

Global site tag (gtag.js) - Google Analytics