forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathconcepts.dd
More file actions
246 lines (211 loc) · 6.39 KB
/
Copy pathconcepts.dd
File metadata and controls
246 lines (211 loc) · 6.39 KB
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
Ddoc
$(D_S テンプレート制約,
$(P テンプレートの多重定義は、通常、
実テンプレート引数が仮引数にマッチするかどうかという基準で解決されます。
テンプレート仮引数を特殊化することで、
特定の型パターンにしかマッチしないように記述することが可能です。
同様に、
テンプレートの値引数が特定の型の物しかこないように制限することも可能です。)
$(P しかし、これにはまだ制限があります。
テンプレートとして受け取る物を、
任意の複雑な条件で制約したいことは多々あります。
例えばこんな用途が考えられるでしょう:
)
$(UL
$(LI 引数に応じて、
より精細な条件で使うテンプレートのインスタンスを切り替える。)
$(LI テンプレート引数が満たさなければならない条件の、
よりよいドキュメンテーションとして使う。 )
$(LI 引数がマッチしない場合に、
(ユーザーには)無関係なテンプレートの内部実装の詳細に基づいた汚いエラーメッセージではなく、
わかりやすい情報を表示する。)
)
$(P テンプレート制約は、
これらの要求に簡潔に答えるために指定できる、
引数のマッチングが行われた後にコンパイル時評価するとtrueにならなければならない式です。
この式がtrueになると初めてそのテンプレートが引数に対してマッチしたことになり、
そうでなければ、マッチせず、
オーバーロード解決の過程から捨てられます。)
$(P 制約式は、テンプレート宣言のあとに続けて、
予約語 $(CODE if) の後に記述します:)
---
template Foo(int N)
if (N & 1)
{
...
}
---
$(P この例では、$(CODE Foo)
は引数が奇数だった時のみマッチします。
コンパイル時計算可能である限り、どんな複雑な条件を記述することも可能です。
例えば、
以下の例は素数だけを受け取るテンプレートです:
)
---
bool isprime(int n)
{
if (n < 1 || (n & 1) == 0)
return false;
if (n > 3)
{
for (auto i = 3; i * i < n; i += 2)
{
if ((n % i) == 0)
return false;
}
}
return true;
}
template Foo(int N)
if (isprime(N))
{
...
}
Foo!(5) // ok, 5 は素数
Foo!(6) // Foo にマッチしない。
---
$(P 型の制約にも複雑な条件を用いることができます。例えば、
以下のテンプレート
Bar は古くからあるテンプレートの特殊化で浮動小数点型のみにマッチする例ですが:
)
---
template Bar(T:float)
{
...
}
template Bar(T:double)
{
...
}
template Bar(T:real)
{
...
}
---
$(P これではテンプレートの実装を複数箇所に同じ事を書かねばなりません。
テンプレート制約を使えば、
一つのテンプレートにまとめることができます:
)
---
template Bar(T)
if (is(T == float) || is(T == double) || is(T == real))
{
...
}
---
$(P 標準ライブラリ $(CODE std.traits) の $(CODE isFloatingPoint)
を使うともっと簡単に書くことが可能です:
)
---
import std.traits;
template Bar(T)
if (isFloatingPoint!(T))
{
...
}
---
$(P 型の性質についても調べることができます。
例えば、足し算ができる型がどうかのチェックはこうなります:
)
---
// T のインスタンスが足し算可能なら true
template isAddable(T)
{ // 型 T の2つのインスタンスを足し合わせてみる
const isAddable = __traits(compiles, (T t) { return t + t; });
}
int Foo(T)(T t)
if (isAddable!(T))
{
return 3;
}
struct S
{
void opAdd(S s) { } // 加算が定義された構造体
}
void main()
{
Foo(4); // 成功
S s;
Foo(s); // 成功
Foo("a"); // マッチ失敗
}
---
$(P コンパイル時計算可能などんな式も制約に使えるので、
複数の制約を合成することもできます:
)
---
int Foo(T)(T t)
if (isAddable!(T) && isMultipliable!(T))
{
return 3;
}
---
$(P より複雑なタイプの制約としては、
型に関して必要な操作を列挙するというものがあります。例えば、以下の $(CODE isStack)
はスタックとして使うのに必要な操作を指定しています:)
----
template isStack(T)
{
const isStack =
__traits(compiles,
(T t)
{ T.value_type v = top(t);
push(t, v);
pop(t);
if (empty(t)) { }
});
}
template Foo(T)
if (isStack!(T))
{
...
}
----
$(P 制約では複数のパラメタを扱うこともできます:)
---
template Foo(T, int N)
if (isAddable!(T) && isprime(N))
{
...
}
---
<h2>制約によるオーバーロード</h2>
$(P 同じ名前でオーバーロードされたテンプレートの一覧があるときに、
テンプレート制約はその一覧を yes/no
でふるい落としてマッチの対象を絞り込む働きをします。
制約によってオーバーロードを制御するには、したがって、
互いに排他的な制約条件を記述することで行います。
例えば、$(CODE Foo) を奇数と偶数の場合で場合分けするには、
以下のようになります:
)
---
template Foo(int N) if (N & 1) { ... } // A
template Foo(int N) if (!(N & 1)) { ... } // B
...
Foo!(3) // A が使われる
Foo!(64) // B が使われる
---
$(P 制約は、複数のインスタンスのどちらがより制約されているか、
という評価には使用されません。
)
---
void foo(T, int N)() if (N & 1) { ... } // A
void foo(T : int, int N)() if (N > 3) { ... } // B
...
foo!(int, 7)(); // B がより制約されているので使われる
foo!(int, 1)(); // A が使われる。B の制約が満たされないため。
foo!("a", 7)(); // A が使われる。
foo!("a", 4)(); // エラー、マッチしない。
---
<h2>参考文献</h2>
$(UL
$(LI $(LINK2 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2081.pdf, Concepts (Revision 1))
by Douglas Gregor and Bjarne Stroustrup
)
)
)
Macros:
TITLE=テンプレート制約
WIKI=Constraints
META_KEYWORDS=D Programming Language, constraints, template, concepts, C++
META_DESCRIPTION=Going beyond type patterns to constrain template instantiations.