@@ -4,33 +4,44 @@ $(D_S Template Constraints,
44
55 $(P Templates are normally overloaded and matched based on the
66 template arguments being matched to the template parameters.
7- The template parameters can specify specializations, so that
7+ The template parameters can specify
8+ $(DDSUBLINK spec/template, parameters_specialization, specializations), so that
89 the template argument must match particular type patterns.
910 Similarly, template value arguments can be constrained to
1011 match particular types.)
1112
13+ ---
14+ template Foo(T) { ... }
15+ template Foo(T : T*) { ... }
16+ template Foo(T : T[]) { ... }
17+
18+ alias f1 = Foo!(int); // picks Foo(T)
19+ alias f2 = Foo!(int*); // picks Foo(T : T*)
20+ alias f3 = Foo!(int[]); // picks Foo(T : T[])
21+ ---
22+
1223 $(P But this has its limitations. Many times there are
1324 arbitrarily more
1425 complex criteria for what should be accepted by the template.
15- This can be used to:
26+ Such criteria could be to:
1627 )
1728
1829 $(UL
1930 $(LI more finely discriminate about which
2031 template gets instantiated for given arguments)
21- $(LI provides better self-documentation about what
32+ $(LI provide better self-documentation about what
2233 characteristics template parameters must have)
23- $(LI can provide better diagnostics when arguments don't match,
34+ $(LI provide better diagnostics when arguments don't match,
2435 rather than an obscure error message based on the irrelevant
2536 (to the user) internal details of the template implementation)
2637 )
2738
28- $(P Constraints address this by simply providing an
29- expression that must evaluate at compile time to true
39+ $(P Constraints address this by simply providing a boolean
40+ expression that is evaluated at compile- time,
3041 after the arguments are matched to the parameters.
31- If it is true, then that template is a valid match for
32- the arguments, if not, then it is not and is passed over
33- during overload matching.)
42+ If that boolean is true, then the template is a valid match for
43+ the arguments, if not, then the template does not match and
44+ is passed over during overload matching.)
3445
3546 $(P The constraint expression follows the template declaration
3647 and the $(CODE if) keyword:)
@@ -45,20 +56,27 @@ template Foo(int N)
4556
4657 $(P which constrains the template $(CODE Foo) to match only if its
4758 argument
48- is an odd integer. Arbitrarily complex criteria can be used, as
59+ is an odd integer.)
60+
61+ $(H2 $(LNAME2 predicates, Predicate Functions))
62+
63+ $(P Arbitrarily complex criteria can be used, as
4964 long as it can be computed at compile time. For example, here's
5065 a template that only accepts prime numbers:
5166 )
5267
68+ $(RUNNABLE_EXAMPLE_COMPILE
5369---
5470bool isPrime(int n)
5571{
5672 if (n == 2)
5773 return true;
74+ // 0, negative and even numbers are not prime
5875 if (n < 1 || (n & 1) == 0)
5976 return false;
6077 if (n > 3)
6178 {
79+ // check possible odd integer denominators
6280 for (auto i = 3; i * i <= n; i += 2)
6381 {
6482 if ((n % i) == 0)
@@ -71,16 +89,22 @@ bool isPrime(int n)
7189template Foo(int N)
7290 if (isPrime(N))
7391{
74- ...
92+ // ...
7593}
7694
77- Foo!(5) // ok, 5 is prime
78- Foo!(6) // no match for Foo
95+ alias f1 = Foo!(5); // ok, 5 is prime
96+ //alias f2 = Foo!(6); // error: no match for Foo
97+ //alias f3 = Foo!(9); // error: no match for Foo
7998---
99+ )
100+
101+ $(NOTE `isPrime` is called using $(DDSUBLINK spec/function, interpretation, Compile-Time Function Evaluation).)
80102
81- $(P Type constraints can be complex, too. For example, a template
82- Bar that will accept any floating point type using the traditional
83- type specializations:
103+ $(H2 $(LNAME2 is, `is` Expressions))
104+
105+ $(P Type constraints can be complex, too. With type specialization alone, a template
106+ `Bar` that will accept any type that implicitly converts to a built-in floating
107+ point type must use template overloads:
84108 )
85109---
86110template Bar(T:float)
@@ -102,84 +126,127 @@ template Bar(T:real)
102126 )
103127---
104128template Bar(T)
105- if (is(T == float) || is(T == double) || is(T == real))
129+ if (is(T : float) || is(T : double) || is(T : real))
106130{
107131 ...
108132}
109133---
110- $(P This can be simplified by using the $(CODE isFloatingPoint)
111- template in library module $(CODE std.traits):
134+ $(P Unlike with parameter specialization, types with implicit conversion to
135+ floating point can be ruled out with a different constraint:)
136+
137+ $(RUNNABLE_EXAMPLE_COMPILE
138+ ---
139+ template Bar(T)
140+ if (is(T == float) || is(T == double) || is(T == real))
141+ {
142+ // ...
143+ }
144+
145+ alias b1 = Bar!float; // OK
146+ //alias b2 = Bar!int; // error
147+ ---
148+ )
149+ $(P See $(GLINK2 expression, IsExpression) for more tests.)
150+
151+ $(P The above example can be simplified by using the $(CODE isFloatingPoint)
152+ template in library module $(MREF std, traits):
112153 )
113154---
114155import std.traits;
156+
115157template Bar(T)
116158 if (isFloatingPoint!(T))
117159{
118160 ...
119161}
120162---
121163
164+ $(H2 $(LNAME2 traits, `__traits`))
165+
122166 $(P Characteristics of types can be tested, such as if
123- a type can be added:
167+ a type instance can be added:
124168 )
125169
170+ $(RUNNABLE_EXAMPLE
126171---
127172// Returns true if instances of type T can be added
128- template isAddable(T)
129- {
130- // Works by attempting to add two instances of type T
131- const isAddable = __traits(compiles, (T t) { return t + t; });
132- }
173+ // Works by attempting to add two instances of type T
174+ const isAddable(T) = __traits(compiles, (T t) { return t + t; });
133175
134- int Foo (T)(T t)
176+ auto twice (T)(T t)
135177 if (isAddable!(T))
136178{
137- return 3 ;
179+ return t + t ;
138180}
139181
182+ // an addable struct type
140183struct S
141184{
142- void opAdd(S s) { } // an addable struct type
185+ int i;
186+
187+ S opBinary(string op : "+")(S s)
188+ {
189+ return S(i + s.i);
190+ }
143191}
144192
145193void main()
146194{
147- Foo(4); // succeeds
148- S s;
149- Foo(s); // succeeds
150- Foo ("a"); // fails to match
195+ assert(twice(4) == 8);
196+ S s = {2} ;
197+ assert(twice(s).i == 4);
198+ //twice ("a"); // fails to match
151199}
152200---
201+ )
202+
203+ $(P $(DDSUBLINK spec/traits, compiles, `__traits(compiles)`) is
204+ used to check if a function literal successfully compiles. Other
205+ expressions can be used instead of a function literal. The expression
206+ is not evaluated.
207+ Other compile-time `__traits` $(DDLINK spec/traits, Traits, are available).)
208+
209+ $(NOTE The function literal above declares an argument of type
210+ `T` to obtain an instance of `T` without having to construct it.)
153211
154212 $(P Since any expression that can be computed at compile time
155213 is allowed as a constraint, constraints can be composed:
156214 )
157215
158216---
159- int Foo (T)(T t)
217+ T foo (T)(T t)
160218 if (isAddable!(T) && isMultipliable!(T))
161219{
162- return 3 ;
220+ return t + t * t ;
163221}
164222---
165223
166- $(P A more complex constraint can specify a list of operations
167- that must be doable with the type, such as $(CODE isStack) which
168- specifies the constraints that a stack type must have:)
224+ $(P Constraints can deal with multiple parameters:)
169225
170- ----
171- template isStack(T)
226+ ---
227+ template Foo(T, int N)
228+ if (isAddable!(T) && isPrime(N))
172229{
173- const isStack =
174- __traits(compiles,
175- (T t)
176- {
177- T.value_type v = top(t);
178- push(t, v);
179- pop(t);
180- if (empty(t)) { }
181- });
230+ ...
182231}
232+ ---
233+
234+ $(P A more complex constraint can specify a list of operations
235+ that must be doable with the type, such as evaluating template $(CODE isStack) which
236+ requires that the type has a type property `ValueType`, and that 4 functions
237+ exist which take an instance of the type, 2 of which return certain
238+ values:)
239+
240+ ----
241+ const isStack(T) =
242+ __traits(compiles,
243+ (T t)
244+ {
245+ T.ValueType v = top(t);
246+ push(t, v);
247+ pop(t);
248+ if (empty(t)) { }
249+ });
183250
184251template Foo(T)
185252 if (isStack!(T))
@@ -188,17 +255,7 @@ template Foo(T)
188255}
189256----
190257
191- $(P and constraints can deal with multiple parameters:)
192-
193- ---
194- template Foo(T, int N)
195- if (isAddable!(T) && isprime(N))
196- {
197- ...
198- }
199- ---
200-
201- $(H2 Overloading based on Constraints)
258+ $(H2 $(LNAME2 overloading, Overloading based on Constraints))
202259
203260 $(P Given a list of overloaded templates with the same name,
204261 constraints act as a yes/no filter to determine the list
@@ -216,6 +273,17 @@ Foo!(3) // instantiates A
216273Foo!(64) // instantiates B
217274---
218275
276+ $(P Note the above 2 templates could be combined using
277+ $(DDSUBLINK spec/version, staticif, `static if`):)
278+ ---
279+ template Foo(int N)
280+ {
281+ static if (N & 1)
282+ // body of A
283+ else
284+ // body of B
285+ }
286+ ---
219287 $(P Constraints are not involved with determining which
220288 template is more specialized than another.
221289 )
0 commit comments