forked from DlangRen/Programming-in-D
-
Notifications
You must be signed in to change notification settings - Fork 1
/
templates_more.d
2107 lines (1608 loc) · 61 KB
/
templates_more.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Ddoc
$(DERS_BOLUMU $(IX template) More Templates)
$(P
We have seen the power and convenience of templates in $(LINK2 /ders/d.en/templates.html, the Templates chapter). A single templated definition of an algorithm or a data structure is sufficient to use that definition for multiple types.
)
$(P
That chapter covered only the most common uses of templates: function, $(C struct), and $(C class) templates and their uses with $(I type) template parameters. In this chapter we will see templates in more detail. Before going further, I recommend that you review at least the summary section of that chapter.
)
$(H5 $(IX shortcut syntax, template) The shortcut syntax)
$(P
In addition to being powerful, D templates are easy to define and use and they are very readable. Defining a function, $(C struct), or $(C class) template is as simple as providing a template parameter list:
)
---
T twice$(HILITE (T))(T value) {
return 2 * value;
}
class Fraction$(HILITE (T)) {
T numerator;
T denominator;
// ...
}
---
$(P
Template definitions like the ones above are taking advantage of D's shortcut template syntax.
)
$(P
In their full syntax, templates are defined by the $(C template) keyword. The equivalents of the two template definitions above are the following:
)
---
template twice$(HILITE (T)) {
T twice(T value) {
return 2 * value;
}
}
template Fraction$(HILITE (T)) {
class Fraction {
T numerator;
T denominator;
// ...
}
}
---
$(P
Although most templates are defined by the shortcut syntax, the compiler always uses the full syntax. We can imagine the compiler applying the following steps to convert a shortcut syntax to its full form behind the scenes:
)
$(OL
$(LI Wrap the definition with a template block.)
$(LI Give the same name to that block.)
$(LI Move the template parameter list to the template block.)
)
$(P
The full syntax that is arrived after those steps is called an $(I eponymous template), which the programmer can define explicitly as well. We will see eponymous templates later below.
)
$(H6 $(IX name space, template) Template name space)
$(P
It is possible to have more than one definition inside a template block. The following template contains both a function and a $(C struct) definition:
)
---
template MyTemplate(T) {
T foo(T value) {
return value / 3;
}
struct S {
T member;
}
}
---
$(P
Instantiating the template for a specific type instantiates all of the definitions inside the block. The following code instantiates the template for $(C int) and $(C double):
)
---
auto result = $(HILITE MyTemplate!int).foo(42);
writeln(result);
auto s = $(HILITE MyTemplate!double).S(5.6);
writeln(s.member);
---
$(P
A specific instantiation of a template introduces a $(I name space). The definitions that are inside an instantiation can be used by that name. However, if these names are too long, it is always possible to use aliases as we have seen in $(LINK2 /ders/d.en/alias.html, the $(C alias) chapter):
)
---
alias MyStruct = MyTemplate!dchar.S;
// ...
auto o = $(HILITE MyStruct)('a');
writeln(o.member);
---
$(H6 $(IX eponymous template) Eponymous templates)
$(P
Eponymous templates are $(C template) blocks that contain a definition that has the same name as that block. In fact, each shortcut template syntax is the shortcut of an eponymous template.
)
$(P
As an example, assume that a program needs to qualify types that are larger than 20 bytes as $(I too large). Such a qualification can be achieved by a constant $(C bool) value inside a template block:
)
---
template isTooLarge(T) {
enum isTooLarge = T.sizeof > 20;
}
---
$(P
Note how the names of both the template block and its only definition are the same. This eponymous template is used by the shortcut syntax instead of the whole $(C isTooLarge!int.isTooLarge):
)
---
writeln($(HILITE isTooLarge!int));
---
$(P
The highlighted part above is the same as the $(C bool) value inside the block. Since the size of $(C int) is less than 20, the output of the code would be $(C false).
)
$(P
That eponymous template can be defined by the shortcut syntax as well:
)
---
enum isTooLarge$(HILITE (T)) = T.sizeof > 20;
---
$(P
A common use of eponymous templates is defining type aliases depending on certain conditions. For example, the following eponymous template picks the larger of two types by setting an alias to it:
)
---
$(CODE_NAME LargerOf)template LargerOf(A, B) {
static if (A.sizeof < B.sizeof) {
alias LargerOf = B;
} else {
alias LargerOf = A;
}
}
---
$(P
Since $(C long) is larger than $(C int) (8 bytes versus 4 bytes), $(C LargerOf!(int, long)) would be the same as the type $(C long). Such templates are especially useful in other templates where the two types are template parameters themselves (or depend on template parameters):
)
---
$(CODE_XREF LargerOf)// ...
/* The return type of this function is the larger of its two
* template parameters: Either type A or type B. */
auto calculate(A, B)(A a, B b) {
$(HILITE LargerOf!(A, B)) result;
// ...
return result;
}
void main() {
auto f = calculate(1, 2$(HILITE L));
static assert(is (typeof(f) == $(HILITE long)));
}
---
$(H5 Kinds of templates)
$(H6 Function, class, and struct templates)
$(P
We have already covered function, $(C class), and $(C struct) templates in $(LINK2 /ders/d.en/templates.html, the Templates chapter) and we have seen many examples of them since then.
)
$(H6 $(IX member function template) Member function templates)
$(P
$(C struct) and $(C class) member functions can be templates as well. For example, the following $(C put()) member function template would work with any parameter type as long as that type is compatible with the operations inside the template (for this specific template, it should be convertible to $(C string)):
)
---
class Sink {
string content;
void put$(HILITE (T))(auto ref const T value) {
import std.conv;
content ~= value.to!string;
}
}
---
$(P
However, as templates can have potentially infinite number of instantiations, they cannot be $(LINK2 /ders/d.en/inheritance.html, virtual functions) because the compiler cannot know which specific instantiations of a template to include in the interface. (Accordingly, the $(C abstract) keyword cannot be used either.)
)
$(P
For example, although the presence of the $(C put()) template in the following subclass may give the impression that it is overriding a function, it actually hides the $(C put) name of the superclass (see $(I name hiding) in $(LINK2 /ders/d.en/alias.html, the alias chapter)):
)
---
class Sink {
string content;
void put(T)(auto ref const T value) {
import std.conv;
content ~= value.to!string;
}
}
class SpecialSink : Sink {
/* The following template definition does not override
* the template instances of the superclass; it hides
* those names. */
void put(T)(auto ref const T value) {
import std.string;
super.put(format("{%s}", value));
}
}
void fillSink($(HILITE Sink) sink) {
/* The following function calls are not virtual. Because
* parameter 'sink' is of type 'Sink', the calls will
* always be dispatched to Sink's 'put' template
* instances. */
sink.put(42);
sink.put("hello");
}
void main() {
auto sink = new $(HILITE SpecialSink)();
fillSink(sink);
import std.stdio;
writeln(sink.content);
}
---
$(P
As a result, although the object actually is a $(C SpecialSink), both of the calls inside $(C fillSink()) are dispatched to $(C Sink) and the content does not contain the curly brackets that $(C SpecialSink.put()) inserts:
)
$(SHELL
42hello $(SHELL_NOTE Sink's behavior, not SpecialSink's)
)
$(H6 $(IX union template) Union templates)
$(P
Union templates are similar to struct templates. The shortcut syntax is available for them as well.
)
$(P
As an example, let's design a more general version of the $(C IpAdress) $(C union) that we saw in $(LINK2 /ders/d.en/union.html, the Unions chapter). There, the value of the IPv4 address was kept as a $(C uint) member in that earlier version of $(C IpAdress), and the element type of the segment array was $(C ubyte):
)
---
union IpAddress {
uint value;
ubyte[4] bytes;
}
---
$(P
The $(C bytes) array provided easy access to the four segments of the IPv4 address.
)
$(P
The same concept can be implemented in a more general way as the following $(C union) template:
)
---
union SegmentedValue($(HILITE ActualT, SegmentT)) {
ActualT value;
SegmentT[/* number of segments */] segments;
}
---
$(P
That template would allow specifying the types of the value and its segments freely.
)
$(P
The number of segments that are needed depends on the types of the actual value and the segments. Since an IPv4 address has four $(C ubyte) segments, that value was hard-coded as $(C 4) in the earlier definition of $(C IpAddress). For the $(C SegmentedValue) template, the number of segments must be computed at compile time when the template is instantiated for the two specific types.
)
$(P
The following eponymous template takes advantage of the $(C .sizeof) properties of the two types to calculate the number of segments needed:
)
---
$(CODE_NAME segmentCount)template segmentCount(ActualT, SegmentT) {
enum segmentCount = ((ActualT.sizeof + (SegmentT.sizeof - 1))
/ SegmentT.sizeof);
}
---
$(P
The shortcut syntax may be more readable:
)
---
enum segmentCount(ActualT, SegmentT) =
((ActualT.sizeof + (SegmentT.sizeof - 1))
/ SegmentT.sizeof);
---
$(P
$(I $(B Note:) The expression $(C SegmentT.sizeof - 1) is for when the sizes of the types cannot be divided evenly. For example, when the actual type is 5 bytes and the segment type is 2 bytes, even though a total of 3 segments are needed, the result of the integer division 5/2 would incorrectly be 2.)
)
$(P
The definition of the union template is now complete:
)
---
$(CODE_NAME SegmentedValue)$(CODE_XREF segmentCount)union SegmentedValue(ActualT, SegmentT) {
ActualT value;
SegmentT[segmentCount!(ActualT, SegmentT)] segments;
}
---
$(P
Instantiation of the template for $(C uint) and $(C ubyte) would be the equivalent of the earlier definition of $(C IpAddress):
)
---
$(CODE_XREF SegmentedValue)import std.stdio;
void main() {
auto address = SegmentedValue!($(HILITE uint, ubyte))(0xc0a80102);
foreach (octet; address.segments) {
write(octet, ' ');
}
}
---
$(P
The output of the program is the same as the one in $(LINK2 /ders/d.en/union.html, the Unions chapter):
)
$(SHELL_SMALL
2 1 168 192
)
$(P
To demonstrate the flexibility of this template, let's imagine that it is required to access the parts of the IPv4 address as two $(C ushort) values. It would be as easy as providing $(C ushort) as the segment type:
)
---
auto address = SegmentedValue!(uint, $(HILITE ushort))(0xc0a80102);
---
$(P
Although unusual for an IPv4 address, the output of the program would consist of two $(C ushort) segment values:
)
$(SHELL_SMALL
258 49320
)
$(H6 $(IX interface template) Interface templates)
$(P
Interface templates provide flexibility on the types that are used on an interface (as well as values such as sizes of fixed-length arrays and other features of an interface).
)
$(P
Let's define an interface for colored objects where the type of the color is determined by a template parameter:
)
---
interface ColoredObject(ColorT) {
void paint(ColorT color);
}
---
$(P
That interface template requires that its subtypes must define the $(C paint()) function but it leaves the type of the color flexible.
)
$(P
A class that represents a frame on a web page may choose to use a color type that is represented by its red, green, and blue components:
)
---
struct RGB {
ubyte red;
ubyte green;
ubyte blue;
}
class PageFrame : ColoredObject$(HILITE !RGB) {
void paint(RGB color) {
// ...
}
}
---
$(P
On the other hand, a class that uses the frequency of light can choose a completely different type to represent color:
)
---
alias Frequency = double;
class Bulb : ColoredObject$(HILITE !Frequency) {
void paint(Frequency color) {
// ...
}
}
---
$(P
However, as explained in $(LINK2 /ders/d.en/templates.html, the Templates chapter), "every template instantiation yields a distinct type". Accordingly, the interfaces $(C ColoredObject!RGB) and $(C ColoredObject!Frequency) are unrelated interfaces, and $(C PageFrame) and $(C Bulb) are unrelated classes.
)
$(H5 $(IX parameter, template) Kinds of template parameters)
$(P
The template parameters that we have seen so far have all been $(I type) parameters. So far, parameters like $(C T) and $(C ColorT) all represented types. For example, $(C T) meant $(C int), $(C double), $(C Student), etc. depending on the instantiation of the template.
)
$(P
There are other kinds of template parameters: value, $(C this), $(C alias), and tuple.
)
$(H6 $(IX type template parameter) Type template parameters)
$(P
This section is only for completeness. All of the templates that we have seen so far had type parameters.
)
$(H6 $(IX value template parameter) Value template parameters)
$(P
Value template parameters allow flexibility on certain values used in the template implementation.
)
$(P
Since templates are a compile-time feature, the values for the value template parameters must be known at compile time; values that must be calculated at run time cannot be used.
)
$(P
To see the advantage of value template parameters, let's start with a set of structs representing geometric shapes:
)
---
struct Triangle {
Point[3] corners;
// ...
}
struct Rectangle {
Point[4] corners;
// ...
}
struct Pentagon {
Point[5] corners;
// ...
}
---
$(P
Let's assume that other member variables and member functions of those types are exactly the same and that the only difference is the $(I value) that determines the number of corners.
)
$(P
Value template parameters help in such cases. The following struct template is sufficient to represent all of the types above and more:
)
---
struct Polygon$(HILITE (size_t N)) {
Point[N] corners;
// ...
}
---
$(P
The only template parameter of that struct template is a value named $(C N) of type $(C size_t). The value $(C N) can be used as a compile-time constant anywhere inside the template.
)
$(P
That template is flexible enough to represent shapes of any sides:
)
---
auto centagon = Polygon!100();
---
$(P
The following aliases correspond to the earlier struct definitions:
)
---
alias Triangle = Polygon!3;
alias Rectangle = Polygon!4;
alias Pentagon = Polygon!5;
// ...
auto triangle = Triangle();
auto rectangle = Rectangle();
auto pentagon = Pentagon();
---
$(P
The type of the $(I value) template parameter above was $(C size_t). As long as the value can be known at compile time, a value template parameter can be of any type: a fundamental type, a $(C struct) type, an array, a string, etc.
)
---
struct S {
int i;
}
// Value template parameter of struct S
void foo($(HILITE S s))() {
// ...
}
void main() {
foo!(S(42))(); // Instantiating with literal S(42)
}
---
$(P
The following example uses a $(C string) template parameter to represent an XML tag to produce a simple XML output:
)
$(UL
$(LI First the tag between the $(C <) $(C >) characters: $(C <tag>))
$(LI Then the value)
$(LI Finally the tag between the $(C </) $(C >) characters: $(C </tag>))
)
$(P
For example, an XML tag representing $(I location 42) would be printed as $(C <location>42</location>).
)
---
$(CODE_NAME XmlElement)import std.string;
class XmlElement$(HILITE (string tag)) {
double value;
this(double value) {
this.value = value;
}
override string toString() const {
return format("<%s>%s</%s>", tag, value, tag);
}
}
---
$(P
Note that the template parameter is not about a type that is used in the implementation of the template, rather it is about a $(C string) $(I value). That value can be used anywhere inside the template as a $(C string).
)
$(P
The XML elements that a program needs can be defined as aliases as in the following code:
)
---
$(CODE_XREF XmlElement)alias Location = XmlElement!"location";
alias Temperature = XmlElement!"temperature";
alias Weight = XmlElement!"weight";
void main() {
Object[] elements;
elements ~= new Location(1);
elements ~= new Temperature(23);
elements ~= new Weight(78);
writeln(elements);
}
---
$(P
输出:
)
$(SHELL_SMALL
[<location>1</location>, <temperature>23</temperature>, <weight>78</weight>]
)
$(P
Value template parameters can have default values as well. For example, the following struct template represents points in a multi-dimensional space where the default number of dimensions is 3:
)
---
struct Point(T, size_t dimension $(HILITE = 3)) {
T[dimension] coordinates;
}
---
$(P
That template can be used without specifying the $(C dimension) template parameter:
)
---
Point!double center; // a point in 3-dimensional space
---
$(P
The number of dimensions can still be specified when needed:
)
---
Point!(int, 2) point; // a point on a surface
---
$(P
We have seen in $(LINK2 /ders/d.en/parameter_flexibility.html, the Variable Number of Parameters chapter) how $(I special keywords) work differently depending on whether they appear inside code or as default function arguments.
)
$(P
Similarly, when used as default template arguments, the special keywords refer to where the template is instantiated at, not where the keywords appear:
)
---
import std.stdio;
void func(T,
string functionName = $(HILITE __FUNCTION__),
string file = $(HILITE __FILE__),
size_t line = $(HILITE __LINE__))(T parameter) {
writefln("Instantiated at function %s at file %s, line %s.",
functionName, file, line);
}
void main() {
func(42); $(CODE_NOTE $(HILITE line 12))
}
---
$(P
Although the special keywords appear in the definition of the template, their values refer to $(C main()), where the template is instantiated at:
)
$(SHELL
Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 12).
)
$(P
We will use $(C __FUNCTION__) below in a multi-dimensional operator overloading example.
)
$(H6 $(IX this, template parameter) $(C this) template parameters for member functions)
$(P
Member functions can be templates as well. Their template parameters have the same meanings as other templates.
)
$(P
However, unlike other templates, member function template parameters can also be $(I $(C this) parameters). In that case, the identifier that comes after the $(C this) keyword represents the exact type of the $(C this) reference of the object. ($(I $(C this) reference) means the object itself, as is commonly written in constructors as $(C this.member = value).)
)
---
struct MyStruct(T) {
void foo($(HILITE this OwnType))() const {
writeln("Type of this object: ", OwnType.stringof);
}
}
---
$(P
The $(C OwnType) template parameter is the actual type of the object that the member function is called on:
)
---
auto m = MyStruct!int();
auto c = const(MyStruct!int)();
auto i = immutable(MyStruct!int)();
m.foo();
c.foo();
i.foo();
---
$(P
输出:
)
$(SHELL_SMALL
Type of this object: MyStruct!int
Type of this object: const(MyStruct!int)
Type of this object: immutable(MyStruct!int)
)
$(P
As you can see, the type includes the corresponding type of $(C T) as well as the type qualifiers like $(C const) and $(C immutable).
)
$(P
The $(C struct) (or $(C class)) need not be a template. $(C this) template parameters can appear on member function templates of non-templated types as well.
)
$(P
$(C this) template parameters can be useful in $(I template mixins) as well, which we will see two chapters later.
)
$(H6 $(IX alias, template parameter) $(C alias) template parameters)
$(P
$(C alias) template parameters can correspond to any symbol or expression that is used in the program. The only constraint on such a template argument is that the argument must be compatible with its use inside the template.
)
$(P
$(C filter()) and $(C map()) use $(C alias) template parameters to determine the operations that they execute.
)
$(P
Let's see a simple example on a $(C struct) template that is for modifying an existing variable. The $(C struct) template takes the variable as an $(C alias) parameter:
)
---
struct MyStruct(alias variable) {
void set(int value) {
variable = value;
}
}
---
$(P
The member function simply assigns its parameter to the variable that the $(C struct) template is instantiated with. That variable must be specified during the instantiation of the template:
)
---
int x = 1;
int y = 2;
auto object = MyStruct!$(HILITE x)();
object.set(10);
writeln("x: ", x, ", y: ", y);
---
$(P
In that instantiation, the $(C variable) template parameter corresponds to the variable $(C x):
)
$(SHELL_SMALL
x: $(HILITE 10), y: 2
)
$(P
Conversely, $(C MyStruct!y) instantiation of the template would associate $(C variable) with $(C y).
)
$(P
Let's now have an $(C alias) parameter that represents a callable entity, similar to $(C filter()) and $(C map()):
)
---
void caller(alias func)() {
write("calling: ");
$(HILITE func());
}
---
$(P
As seen by the $(C ()) parentheses, $(C caller()) uses its template parameter as a function. Additionally, since the parentheses are empty, it must be legal to call the function without specifying any arguments.
)
$(P
Let's have the following two functions that match that description. They can both represent $(C func) because they can be called as $(C func()) in the template:
)
---
void foo() {
writeln("foo called.");
}
void bar() {
writeln("bar called.");
}
---
$(P
Those functions can be used as the $(C alias) parameter of $(C caller()):
)
---
caller!foo();
caller!bar();
---
$(P
输出:
)
$(SHELL_SMALL
calling: foo called.
calling: bar called.
)
$(P
As long as it matches the way it is used in the template, any symbol can be used as an $(C alias) parameter. As a counter example, using an $(C int) variable with $(C caller()) would cause a compilation error:
)
---
int variable;
caller!variable(); $(DERLEME_HATASI)
---
$(P
The compilation error indicates that the variable does not match its use in the template:
)
$(SHELL_SMALL
Error: $(HILITE function expected before ()), not variable of type int
)
$(P
Although the mistake is with the $(C caller!variable) instantiation, the compilation error necessarily points at $(C func()) inside the $(C caller()) template because from the point of view of the compiler the error is with trying to call $(C variable) as a function. One way of dealing with this issue is to use $(I template constraints), which we will see below.
)
$(P
If the variable supports the function call syntax perhaps because it has an $(C opCall()) overload or it is a function literal, it would still work with the $(C caller()) template. The following example demonstrates both of those cases:
)
---
class C {
void opCall() {
writeln("C.opCall called.");
}
}
// ...
auto o = new C();
caller!o();
caller!({ writeln("Function literal called."); })();
---
$(P
输出:
)
$(SHELL
calling: C.opCall called.
calling: Function literal called.
)
$(P
$(C alias) parameters can be specialized as well. However, they have a different specialization syntax. The specialized type must be specified between the $(C alias) keyword and the name of the parameter:
)
---
import std.stdio;
void foo(alias variable)() {
writefln("The general definition is using '%s' of type %s.",
variable.stringof, typeof(variable).stringof);
}
void foo(alias $(HILITE int) i)() {
writefln("The int specialization is using '%s'.",
i.stringof);
}
void foo(alias $(HILITE double) d)() {
writefln("The double specialization is using '%s'.",
d.stringof);
}
void main() {
string name;
foo!name();
int count;
foo!count();
double length;
foo!length();
}
---
$(P
Also note that $(C alias) parameters make the names of the actual variables available inside the template:
)
$(SHELL
The general definition is using 'name' of type string.
The int specialization is using 'count'.
The double specialization is using 'length'.
)
$(H6 $(IX tuple template parameter) Tuple template parameters)
$(P
We have seen in $(LINK2 /ders/d.en/parameter_flexibility.html, the Variable Number of Parameters chapter) that variadic functions can take any number and any type of parameters. For example, $(C writeln()) can be called with any number of parameters of any type.
)
$(P
$(IX ..., template parameter) $(IX variadic template) Templates can be variadic as well. A template parameter that consists of a name followed by $(C ...) allows any number and kind of parameters at that parameter's position. Such parameters appear as a tuple inside the template, which can be used like an $(C AliasSeq).
)
$(P
Let's see an example of this with a template that simply prints information about every template argument that it is instantiated with:
)
---
$(CODE_NAME info)void info(T...)(T args) {
// ...
}
---
$(P
The template parameter $(C T...) makes $(C info) a $(I variadic template). Both $(C T) and $(C args) are tuples:
)
$(UL
$(LI $(C T) represents the types of the arguments.)
$(LI $(C args) represents the arguments themselves.)
)
$(P
The following example instantiates that function template with three values of three different types:
)
---
$(CODE_XREF info)import std.stdio;
// ...
void main() {
info($(HILITE 1, "abc", 2.3));
}
---
$(P
The following implementation simply prints information about the arguments by iterating over them in a $(C foreach) loop:
)
---
void info(T...)(T args) {
// 'args' is being used like a tuple:
foreach (i, arg; $(HILITE args)) {
writefln("%s: %s argument %s",
i, typeof(arg).stringof, arg);
}
}
---
$(P
输出:
)
$(SHELL_SMALL
0: int argument 1
1: string argument abc
2: double argument 2.3
)
$(P
Note that instead of obtaining the type of each argument by $(C typeof(arg)), we could have used $(C T[i]) as well.
)
$(P
We know that template arguments can be deduced for function templates. That's why the compiler deduces the types as $(C int), $(C string), and $(C double) in the previous program.
)
$(P
However, it is also possible to specify template parameters explicitly. For example, $(C std.conv.to) takes the destination type as an explicit template parameter:
)
---
to!$(HILITE string)(42);
---
$(P
When template parameters are explicitly specified, they can be a mixture of value, type, and other kinds. That flexibility makes it necessary to be able to determine whether each template parameter is a type or not, so that the body of the template can be coded accordingly. That is achieved by treating the arguments as an $(C AliasSeq).
)
$(P
Let's see an example of this in a function template that produces $(C struct) definitions as source code in text form. Let's have this function return the produced source code as $(C string). This function can first take the name of the $(C struct) followed by the types and names of the members specified as pairs: