std.typecons
ソース std/typecons.d
alias Coord = Tuple!(int, "x", int, "y", int, "z"); Coord c; c[1] = 1; // インデックスによるアクセス c.z = 1; // 指定された名前によるアクセス writeln(c); // Coord(0, 1, 1) // 名前は省略可、型は混合可 alias DictEntry = Tuple!(string, int); auto dict = DictEntry("seven", 7); // 要素の型を推測することができる writeln(tuple(2, 3, 4)[1]); // 3 // 型推論は名前でも機能する auto tup = tuple!("x", "y", "z")(2, 3, 4); writeln(tup.y); // 3
class Widget { void foo() const @safe {} } const w1 = new Widget, w2 = new Widget; w1.foo(); // w1 = w2は機能しない; constオブジェクトをリバインドできない auto r = Rebindable!(const Widget)(w1); // rがWidgetオブジェクトであるかのようにメソッドを呼び出す r.foo(); // 別のオブジェクトを参照するようにrをリバインドする r = w2;
- struct
Unique
(T); - リソースの一意な所有権をカプセル化する。
Unique
!T 。destroy T を呼び出す。 destroy の重要な結果の1つは、 のデストラクタを呼び出すことである。 Tのデストラクタを呼び出すことである。 GCで管理されている参照は、デストラクタの間 の他のメンバは、デストラクタ呼び出し中も有効であることが保証されない。 Tの他のメンバ(ファイルハンドルやmalloc メモリへのポインタなど)は、デストラクタ呼び出し中も有効である。 の他のメンバは、デストラクタ呼び出し中も有効である。 これにより、リソース T の非GCリソースの割り当て解除やクリーンアップができる。 もしUnique
!T を元のスコープの外でも保持したい場合は、それを転送することができる。 スコープ外で永続化することが望ましい場合は、それを転送することができる。 この転送は、 を呼び出すことで 明示的に行うこともできるし、暗黙的に行うこともできる。 releaseを呼び出すことで明示的に行うことも、関数からUniqueを返すときに暗黙的に行うこともできる。 関数からUniqueを返す場合などである。リソースT は、ポリモーフィックなクラスオブジェクトか、インターフェースのインスタンスである。 その場合、Uniqueは多相的に振る舞う。 も多相的に振る舞う。 T が値型の場合、Unique
!T は への参照として実装される。 Tへの参照として実装される。Examples:struct S { int i; this(int i){this.i = i;} } Unique!S produce() { // ヒープ上にSの一意なインスタンスを構築する Unique!S ut = new S(5); // 暗黙の所有権移転 return ut; } // 参照によってユニークなリソースを借りる void increment(ref Unique!S ur) { ur.i++; } void consume(Unique!S u2) { writeln(u2.i); // 6 // リソースはここで自動的に削除される } Unique!S u1; assert(u1.isEmpty); u1 = produce(); writeln(u1.i); // 5 increment(u1); writeln(u1.i); // 6 //consume(u1); // エラー: u1はコピーできない // リソースの所有権を譲渡する consume(u1.release); assert(u1.isEmpty);
- alias
RefT
= T; - T への参照を表す。T が値型の場合はT* に解決する。
- Unique!T
create
(A...)(auto ref Aargs
)
if (__traits(compiles, new T(args
))); - Unique を安全に構築できるようにする。 のエイリアスをT が公開しない限り)。 this).
注釈 入れ子の構造体/クラスは作成できない。
Parameters:A args
T のコンストラクタに渡す引数 。static class C {} auto u = Unique!(C).create();
- this(RefT
p
); - r値を取るコンストラクタ。
- r値が単なるl値のビュー(キャスト)でない限り、一意性が保証される。 が単なるl値のビュー(キャストなど)でない限り、一意性が保証される。 典型的な使用法だ:
Unique!Foo f = new Foo;
- this(ref RefT
p
); - l値を取るコンストラクタ。
- これはそのソースをNULLにする。 NULL化により、ソースに以前のエイリアスがない限り、一意性が保証される。 ソースのエイリアスが存在しない限り、一意性が保証される。
- this(U)(Unique!U
u
)
if (is(u
.RefT : RefT)); - コンストラクタ。こちらの型に変換可能な型のUnique を取る?
- ?
-
通常、派生型のUnique r値を基本型の 。 Unique 。
例:
class C : Object {} Unique!C uc = new C; Unique!Object uo = uc.release;
- void
opAssign
(U)(Unique!Uu
)
if (is(u
.RefT : RefT)); - 当方の型に転換可能な型のUnique から所有権を移転する。
- const @property bool
isEmpty
(); - リソースが存在するかどうかを返す。
- Unique
release
(); - Unique r値に所有権を移す。現在のコンテンツをナル化する。 std.algorithm.moveを呼び出すのと同じ。
- struct
Tuple
(Specs...) if (distinctFieldNames!Specs); - 値のタプル、例えばTuple!(int, string) は、 と を格納するレコードである。 int string
Tuple
は、値を束ねるために使われる。 特に、関数から複数の値を返すときなどである。 関数" から複数の値を返すときなどである。 が の場合、個々のメンバには、 の構文でアクセスできる。 である場合、個々のメンバには、最初のフィールドは という構文でアクセスできる、 でアクセスできる。objTuple
obj[0] obj[1]See Also:Parameters:Specs Tuple
に含まれる型(オプションでメンバ名)のリスト。Examples:Tuple!(int, int) point; // 座標を割り当てる point[0] = 5; point[1] = 6; // 座標を読み込む auto x = point[0]; auto y = point[1];
Examples:付けることができる。Tuple
メンバには名前を名前付きと名前なしの メンバを混在させることは合法である。上記の方法はすべてのフィールドに適用できる。alias Entry = Tuple!(int, "index", string, "value"); Entry e; e.index = 4; e.value = "Hello"; writeln(e[1]); // "Hello" writeln(e[0]); // 4
Examples:である。名前付きフィールドを持つ は、名前なしフィールドを持つ とは別の型Tuple
Tuple
すなわち、それぞれの命名が、 に別個の型を与える。 名前だけが異なる2つのフィールドは、たとえ同じ構造を持っていたとしても、やはり別個のものである。 たとえ同じ構造であってもである。Tuple
Tuple
Tuple!(int, "x", int, "y") point1; Tuple!(int, int) point2; assert(!is(typeof(point1) == typeof(point2)));
Examples:タプルを範囲として使うimport std.algorithm.iteration : sum; import std.range : only; auto t = tuple(1, 2); writeln(t.expand.only.sum); // 3
Examples:タプルを連結するimport std.meta : AliasSeq; auto t = tuple(1, "2") ~ tuple(ushort(42), true); static assert(is(t.Types == AliasSeq!(int, string, ushort, bool))); writeln(t[1]); // "2" writeln(t[2]); // 42 writeln(t[3]); // true
- alias
Types
= staticMap!(extractType, fieldSpecs); - Tuple の構成要素の型。
- alias
fieldNames
= staticMap!(extractName, fieldSpecs); - Tuple の構成要素の名前。名前のないフィールドは空の名前を持つ。Examples:
import std.meta : AliasSeq; alias Fields = Tuple!(int, "id", string, float); static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
- Types
expand
; - Tuple を構成要素に展開するには を使うt t.
expand
。 - に展開する。 の結果は、あたかも の構成要素が値のリストとして列挙されているかのように動作する。 が値のリストであるかのように動作する。(通常、 は単一の値として動作する。 として動作する)。
expand
Tuple TupleExamples:auto t1 = tuple(1, " hello ", 'a'); writeln(t1.toString()); // `Tuple!(int, string, char)(1, " hello ", 'a')` void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); } auto t2 = tuple(4, "test", false); //t.expandは値のリストとして機能する takeSeveralTypes(t2.expand);
- this(Types
values
); - 各フィールドに1つの値を取るコンストラクタ。
-
Parameters:
Types values
。 で与えられる値と同じ型である値のリスト。 で与えられる型と同じか、暗黙のうちに変換できる値のリストである。 に暗黙的に変換できる値のリスト。これらは、 に現れるのと同じ順序でなければならない。 に現れるのと同じ順序でなければならない。Types Tuple Types Examples:alias ISD = Tuple!(int, string, double); auto tup = ISD(1, "test", 3.2); writeln(tup.toString()); // `Tuple!(int, string, double)(1, "test", 3.2)`
- this(U, size_t n)(U[n]
values
)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types)); - 互換性のある配列を取るコンストラクタ。Parameters:
U[n] values
Tuple を構築するための互換性のある静的配列。 配列スライスはサポートされていない。 Examples:int[2] ints; Tuple!(int, int) t = ints;
- this(U)(U
another
)
if (areBuildCompatibleTuples!(typeof(this), U) && (noMemberHasCopyCtor!(typeof(this)) || !is(Unqual!U == Unqual!(typeof(this))))); - 互換性のある を取るコンストラクタTuple。
- 2つの は互換性がある。 二つの は互換性がある。 左辺の各型 に対して、右辺の対応する型 は暗黙的に に変換できる。 に暗黙的に変換できる。Tuple iff T U TParameters:
U another
互換性のある 。その型は次のものでなければならない。 の型と互換性がなければならない。Tuple Tuple Examples:alias IntVec = Tuple!(int, int, int); alias DubVec = Tuple!(double, double, double); IntVec iv = tuple(1, 1, 1); // OK、intは暗黙的にdoubleに変換できる DubVec dv = iv; //エラー: doubleは暗黙的にintに変換できない //IntVec iv2 = dv;
- bool
opEquals
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "=="));
const boolopEquals
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "=="));
boolopEquals
(R...)(auto ref Rrhs
)
if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "==")); - 等しいかどうかの比較。2つの は等しいとみなされる。 それらは以下の条件を満たす:Tuple iff
- 各 は同じ長さであTuple
- る。 左辺の各型 と右辺の各型 について、T 以下の条件を満たす。
- について、 型の値をU T
- 型の値と比較することができる。
U
- の値と比較できる。 左辺の各値 と右辺の各値 について、式 は、右辺の各値 と左辺の各値v1 を比較することができる。
- に対して、式 は真である。 となる。v2 v1 == v2
Parameters:R rhs
比較する 。これは を満たしていなければならない。Tuple Tuple Returns:。両方の が等しければ真、そうでなければ偽TupleExamples:Tuple!(int, string) t1 = tuple(1, "test"); Tuple!(double, string) t2 = tuple(1.0, "test"); //OK、intはdoubleと比較でき、 //どちらも値は1である writeln(t1); // t2
- auto
opCmp
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "<"));
const autoopCmp
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "<")); - 順序の比較。Parameters:
R rhs
Tuple と比較する。それは以下の基準を満たさなければならない。 でなければならない。Tuple Returns:次のようにする。左辺のタプルに含まれる値 と、右辺のタプルに含まれる値 について、v1右辺に含まれる値 : すべてのメンバに対して 、あるいは、最初の位置に対して以下の値が指定された場合は0とする。 を指定する: v2 v1 == v2- オペランドのいずれかが NaN の場合は NaN
- 式 が真の場合は負の数。v1 < v2
- 式 が真の場合は正の数。v1 > v2
Examples:。v1 > v2 が真である最初の が結果を決定するv1が結果を決定する。これは予期せぬ動作につながる可能性がある。auto tup1 = tuple(1, 1, 1); auto tup2 = tuple(1, 100, 100); assert(tup1 < tup2); //比較に重要なのは最初の結果だけである tup1[0] = 2; assert(tup1 > tup2);
- auto
opBinary
(string op, T)(auto ref Tt
)
if (op == "~" && !(is(T : U[], U) && isTuple!U));
autoopBinaryRight
(string op, T)(auto ref Tt
)
if (op == "~" && !(is(T : U[], U) && isTuple!U)); - タプルを連結する。 タプルの連結は、すべての名前付きフィールドが異なる場合にのみ許可される(このタプルの名前付きフィールドが および の名前付きフィールドがこのタプルに存在しない)。
t
t
Parameters:T t
で連結する 。Tuple Returns:このタプルと
t
- ref Tuple
opAssign
(R)(auto ref Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "=")); - 別の からの代入。TupleParameters:
R rhs
Tuple から割り当てる。の各要素は ソース の各要素は、ターゲット の各要素に暗黙的に割り当て可能でなければならない。 の各要素に暗黙的に割り当て可能でなければならない。Tuple Tuple - inout ref auto
rename
(names...)() return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))); - Tuple の要素名を変更する。
-
rename
渡された 。 を返す。 は変更されない。 渡された名前が、 のメンバ数より少ない場合 のメンバの数より少ない名前が渡された場合、それらの末尾のメンバは変更されない。 空文字列は、そのメンバーの名前を削除する。 を超える名前を渡すことはコンパイル時のエラーである。 を超える名前を渡すのはコンパイル時のエラーである。names Tuple Tuple TupleExamples:auto t0 = tuple(4, "hello"); auto t0Named = t0.rename!("val", "tag"); writeln(t0Named.val); // 4 writeln(t0Named.tag); // "hello" Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!"height"; t1Named.height = 3.4f; writeln(t1Named.height); // 3.4f writeln(t1Named.pos); // [2, 1] t1Named.rename!"altitude".altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, int, "c") t2; t2 = tuple(3,4,5); auto t2Named = t2.rename!("", "b"); // "a"にはもはや名前がない static assert(!__traits(hasMember, typeof(t2Named), "a")); writeln(t2Named[0]); // 3 writeln(t2Named.b); // 4 writeln(t2Named.c); // 5 // タプルのメンバ数以上の名前を指定できない static assert(!__traits(compiles, t2.rename!("a","b","c","d"))); // レンジパイプラインで使用する import std.range : iota, zip; import std.algorithm.iteration : map, sum; auto res = zip(iota(1, 4), iota(10, 13)) .map!(t => t.rename!("a", "b")) .map!(t => t.a * t.b) .sum; writeln(res); // 68 const tup = Tuple!(int, "a", int, "b")(2, 3); const renamed = tup.rename!("c", "d"); writeln(renamed.c + renamed.d); // 5
- inout ref auto
rename
(alias translate)()
if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t))); - rename のオーバーロード。 をテンプレートパラメータとして受け取る。 キーが変更されるメンバの名前またはインデックスであり 新しい名前は対応する値である。 、すべてのキーは . 空文字列のルールは、"可変長"の場合と同じである。 のテンプレート・オーバーロードと同じルールが適用される。translate translate tuple renameExamples:
// 名前を現在の名前に置き換える Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!(["dat": "height"]); t1Named.height = 3.4; writeln(t1Named.pos); // [2, 1] t1Named.rename!(["height": "altitude"]).altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, "b") t2; t2 = tuple(3, 4); auto t2Named = t2.rename!(["a": "b", "b": "c"]); writeln(t2Named.b); // 3 writeln(t2Named.c); // 4 const t3 = Tuple!(int, "a", int, "b")(3, 4); const t3Named = t3.rename!(["a": "b", "b": "c"]); writeln(t3Named.b); // 3 writeln(t3Named.c); // 4
Examples:// ポジションで名前を置き換える Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!([0: "height"]); t1Named.height = 3.4; writeln(t1Named.pos); // [2, 1] t1Named.rename!([0: "altitude"]).altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, "b", int, "c") t2; t2 = tuple(3, 4, 5); auto t2Named = t2.rename!([0: "c", 2: "a"]); writeln(t2Named.a); // 5 writeln(t2Named.b); // 4 writeln(t2Named.c); // 3
- inout @property ref @trusted inout(Tuple!(sliceSpecs!(from, to)))
slice
(size_t from, size_t to)()
if (from <= to && (to <= Types.length)); - この の参照スライスを取るTuple。
-
Parameters:
from スライスの開始位置を指定する 。size_t to スライスの終了位置(排他的)を指定する 。size_t Returns:。元の からのスライスである新しい[from, to) Tuple元の の範囲と同じ型と値を持つ。 と同じ型と値を持つ。[from, to)Examples:Tuple!(int, string, float, double) a; a[1] = "abc"; a[2] = 4.5; auto s = a.slice!(1, 3); static assert(is(typeof(s) == Tuple!(string, float))); assert(s[0] == "abc" && s[1] == 4.5); // https://issues.dlang.org/show_bug.cgi?id=15645 Tuple!(int, short, bool, double) b; static assert(!__traits(compiles, b.slice!(2, 4)));
- const nothrow @safe size_t
toHash
(); - この のハッシュを作成Tupleする。
-
Returns:この のハッシュを表す 。Tuple size_t
- const string
toString
()(); - 文字列に変換する。
-
Returns:この の文字列表現。Tuple
- const void
toString
(DG)(scope DGsink
);
const voidtoString
(DG, Char)(scope DGsink
, ref scope const FormatSpec!Charfmt
); - Tuple を 、 、 のいずれかでフォーマット%s %(inner%) %(inner%|sep%)する。
-
タプルがサポートするフォーマット Format Description 。%s
Tuple!(types)(elements formatted with %s each) のようなフォーマット
れる。%(inner%)
フォーマット は、展開された に適用さinner Tuple
、フィールドの数だけ書式を含むことができるTuple
れる。。
%(inner%|sep%)
フォーマット は1つのフォーマットであり、 のすべてのフィールドに適用さinner
フォーマット は、 のすべてのフィールドに適用される1つのフォーマットである。 に互換性がなければならない。Tuple
Parameters:DG sink
char 、デリゲートを受け入れる 。FormatSpec!Char fmt
A
-
Examples:
import std.format : format; Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; // デフォルトのフォーマット writeln(format("%s", tuple("a", 1))); // `Tuple!(string, int)("a", 1)` // 各コンポーネントに1つのフォーマット writeln(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))); // `0x1 v 1.0000 w 0xa` writeln(format("%#x v %.4f w %#x", tuple(1, 1.0, 10).expand)); // `0x1 v 1.0000 w 0xa` // すべてのコンポーネントに1つのフォーマット // `>abc< & >1< & >2.3< & >[4, 5]<` writeln(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5]))); // タプルの配列 writeln(format("%(%(f(%d) = %.1f%); %)", tupList)); // `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`
Examples:import std.exception : assertThrown; import std.format : format, FormatException; // エラー: %( %)が見つからない。 assertThrown!FormatException( format("%d, %f", tuple(1, 2.0)) == `1, 2.0` ); // エラー: %( %| %)が見つからない。 assertThrown!FormatException( format("%d", tuple(1, 2)) == `1, 2` ); // エラー: %dがdoubleに不適切である assertThrown!FormatException( format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` );
reverse
(T)(T t
)if (isTuple!T);
T t |
コピーするTuple | 。
auto tup = tuple(1, "2"); writeln(tup.reverse); // tuple("2", 1)
tuple
(Names...)Names | の連続する各フィールドを示す文字列のリスト(オプション | )。Tuple または、要素がキャストされる型のリスト。 名前のリストの場合、 Args の対応するフィールドと一致する。 すべてのフィールドに名前をつける必要はないが 名前は順番に進まなければならないので、あるフィールドをスキップして、そのフィールドの後に次のフィールドの名前を付けることはできない。 を飛ばして次のフィールドに名前をつけることはできない。 型のリストの場合、 型のリストでは、パラメータと同じ数の型がなければならない。 |
auto value = tuple(5, 6.7, "hello"); writeln(value[0]); // 5 writeln(value[1]); // 6.7 writeln(value[2]); // "hello" // フィールド名を指定できる。 auto entry = tuple!("index", "value")(4, "Hello"); writeln(entry.index); // 4 writeln(entry.value); // "Hello"
- auto
tuple
(Args...)(Argsargs
); - Parameters:
Args args
Tuple を初期化する値。Tuple の"型 "は、与えられた値の型から推測される。 の型は、与えられた値の型から推測される。 Returns:。与えられた引数から型が推測された新しいTuple
isTuple
(T);
T | チェックする型 | 。
static assert(isTuple!(Tuple!())); static assert(isTuple!(Tuple!(int))); static assert(isTuple!(Tuple!(int, real, string))); static assert(isTuple!(Tuple!(int, "x", real, "y"))); static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
Rebindable
(T) if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)struct
Rebindable
(T) if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T);
Rebindable
!(T) はシンプルで効率的なラッパーである。
T 、別のオブジェクトを参照するために再割り当てできることを除けば、 型のオブジェクトと同じように振る舞うシンプルで効率的なラッパーである。
型のオブジェクトと同じように動作するシンプルで効率的なラッパーである。完全を期すため、Rebindable
!(T) は、 が非定数オブジェクト型である場合、 にエイリアスする。
T をT にエイリアスすRebindable
。
例えば、const オブジェクトを参照するストレージを持ちたい場合、 を使いたいかもしれない。
を参照する変更可能なストレージを持ちたい場合、Rebindable
を使いたいかもしれない。
Dの型システムの健全性を壊すことはなく、。
通常、cast に伴うリスクはない。
T | どの型でも | よい。
class Widget { int x; int y() @safe const { return x; } } const a = new Widget; // Fine a.y(); // エラー!const aを変更できない // a.x = 5; // エラー!const aを変更できない // a = new Widget;
Rebindable
!(Widget) class Widget { int x; int y() const @safe { return x; } } auto a = Rebindable!(const Widget)(new Widget); // Fine a.y(); // エラー!const aを変更できない // a.x = 5; // Fine a = new Widget;
import std.range.primitives : front, popFront; // std.algorithm.searching.maxElementのシンプルなバージョン typeof(R.init.front) maxElement(R)(R r) { auto max = rebindable(r.front); r.popFront; foreach (e; r) if (e > max) max = e; // Rebindableはconst-correctの再割り当てを可能にする return max; } struct S { char[] arr; alias arr this; // 比較のために } // ミュータブルに変換できない const S cs; static assert(!__traits(compiles, { S s = cs; })); alias CS = const S; CS[] arr = [CS("harp"), CS("apple"), CS("pot")]; CS ms = maxElement(arr); writeln(ms.arr); // "pot"
static struct S { int* ptr; } S s = S(new int); const cs = s; // s.ptrをcs.ptrに代入できない static assert(!__traits(compiles, {s = cs;})); Rebindable!(const S) rs = s; assert(rs.ptr is s.ptr); // rs.ptrはconstである static assert(!__traits(compiles, {rs.ptr = null;})); // s.ptrをrs.ptrに代入できない static assert(!__traits(compiles, {s = rs;})); const S cs2 = rs; // rsをリバインドする rs = cs2; rs = S(); assert(rs.ptr is null);
rebindable
(T)(T obj
)if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T);
Rebindable!T
rebindable
(T)(T value
)if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T && !is(T : Rebindable!U, U));
T obj |
Rebindable を初期化する値への参照。 |
class C { int payload; this(int p) { payload = p; } } const c = new C(1); auto c2 = c.rebindable; writeln(c2.payload); // 1 // Rebindableをrebindableに渡す c2 = c2.rebindable; c2 = new C(2); writeln(c2.payload); // 2 const c3 = c2.get; writeln(c3.payload); // 2
immutable struct S { int[] array; } auto s1 = [3].idup.rebindable; s1 = [4].idup.rebindable; writeln(s1); // [4]
rebindable
(T)(Rebindable!T obj
);
Rebindable!T obj |
Rebindable!Tのインスタンス | である。
obj
のインスタンスであclass C { int payload; this(int p) { payload = p; } } const c = new C(1); auto c2 = c.rebindable; writeln(c2.payload); // 1 // rebindableをrebindableに渡す c2 = c2.rebindable; writeln(c2.payload); // 1
UnqualRef
(T) if (is(T == class) || is(T == interface))T | クラスまたはインターフェイス型。 |
class Data {} static shared(Data) a; static UnqualRef!(shared Data) b; import core.thread; auto thread = new core.thread.Thread({ a = new shared Data(); b = new shared Data(); }); thread.start(); thread.join(); assert(a !is null); assert(b is null);
alignForSize
(E...)(const char[][] names
...);
E | アラインメントされる型のリスト。 struct class のような集合体のフィールドを表す。 |
char[][] names |
整列されるフィールドの名前。 |
struct Banner { mixin(alignForSize!(byte[6], double)(["name", "height"])); }
Nullable
(T);
auto
nullable
(T)(T t
);
Nullable
!T はT とbool を格納するstruct CustomerRecord { string name; string address; int customerNum; } Nullable!CustomerRecord getByName(string name) { //毛むくじゃらの束 return Nullable!CustomerRecord.init; } auto queryResult = getByName("Doe, John"); if (!queryResult.isNull) { //Mr.Doeの顧客記録を処理する auto address = queryResult.get.address; auto customerNum = queryResult.get.customerNum; //この顧客情報を使っていくつかの処理を行う } else { //顧客をデータベースに追加する }
import std.exception : assertThrown; auto a = 42.nullable; assert(!a.isNull); writeln(a.get); // 42 a.nullify(); assert(a.isNull); assertThrown!Throwable(a.get);
import std.algorithm.iteration : each, joiner; Nullable!int a = 42; Nullable!int b; // 各値を配列に追加する int[] arr; a.each!((n) => arr ~= n); writeln(arr); // [42] b.each!((n) => arr ~= n); writeln(arr); // [42] // Nullableの配列から最初の値を取り出す Nullable!int[] c = new Nullable!int[](10); c[7] = Nullable!int(42); writeln(c.joiner.front); // 42
- inout this(inout T
value
); value
でthis を初期化するコンストラクタ .Parameters:T value
このNullable を初期化する値。 - bool
opEquals
(this This, Rhs)(auto ref Rhsrhs
)
if (!is(CommonType!(This, Rhs) == void));
boolopEquals
(this This, Rhs)(auto ref Rhsrhs
)
if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get ==rhs
))); - 両方がNULLの場合は等しい。一方がNULLで、もう一方がNULLでない場合 がnullでない場合、両者は等しくない。両方がnullでない場合、値が等しければ等しい。 値が等しければ等しい。Examples:
Nullable!int empty; Nullable!int a = 42; Nullable!int b = 42; Nullable!int c = 27; writeln(empty); // empty writeln(empty); // Nullable!int.init assert(empty != a); assert(empty != b); assert(empty != c); writeln(a); // b assert(a != c); assert(empty != 42); writeln(a); // 42 assert(c != 42);
- string
toString
();
const stringtoString
();
voidtoString
(W)(ref Wwriter
, ref scope const FormatSpec!charfmt
)
if (isOutputRange!(W, char));
const voidtoString
(W)(ref Wwriter
, ref scope const FormatSpec!charfmt
)
if (isOutputRange!(W, char)); - isNull がtrue の場合、文字列"Nullable.null" を返す。
- そうでなければ std.format.formattedWrite を呼び出すのと同じである。 を呼び出すのと同じである。Parameters:
W writer
char 。 出力範囲 FormatSpec!char fmt
std.format.FormatSpec 。 このNullableがNULLでない場合の値を表す。 Returns:writer
、fmt
が設定されていない場合はstring 、そうでない場合はvoid 。 - const pure nothrow @property @safe bool
isNull
(); - this がヌル状態かどうかをチェックする。
-
Returns:。trueiff this がヌル状態かどうか、そうでなければ falseExamples:
Nullable!int ni; assert(ni.isNull); ni = 0; assert(!ni.isNull);
- void
nullify
()(); - this を強制的にヌル状態にする。
- Examples:
Nullable!int ni = 0; assert(!ni.isNull); ni.nullify(); assert(ni.isNull);
- ref Nullable
opAssign
()(Tvalue
) return; value
を内部保持状態に割り当てる 。- 割り当てが成功すると this が非NULLになる。Parameters:
T value
このNullable に割り当て る
- 型の値。Examples:ど)をラップしている場合、この にNULL値を代入する。この が、すでにNULL値を持つ型(ポインタなNullable(ポインタなど)をラップしている場合、NULL値をこの の他の値を代入するのと変わらない。 を代入するのと変わらない。その結果、コードは非常に奇妙に見えることになる。 を使用することで、これを避けることを強く推奨する。 を使用することを強く推奨する。 テンプレート引数を取る Nullable T Nullable nullValue
//Passes Nullable!(int*) npi; assert(npi.isNull); //Passes?! npi = null; assert(!npi.isNull);
- inout pure nothrow @property ref @safe inout(T)
get
();
inout @property inout(T)get
()(inout(T)fallback
);
inout @property autoget
(U)(inout(U)fallback
); - nullでなければ値を取得する。
- this がNULLの状態で、オプションの パラメータ が指定されていれば、それが返される。 がない場合、 ヌル状態で を呼び出すことは無効である。
fallback
fallback
get
フォールバック型がNullable型と異なる場合、 。 を返す。get
(T)Parameters:inout(T) fallback
Nullable が NULL の場合に返す値。 Returns:である。この が内部的に保持する値Nullable - alias
empty
= isNull;
aliaspopFront
= nullify;
aliaspopBack
= nullify;
inout pure nothrow @property ref @safe inout(T)front
();
aliasback
= front;
inout @property inout(typeof(this))save
();
inout inout(typeof(this))opIndex
(size_t[2]dim
);
const size_t[2]opSlice
(size_t dim : 0)(size_tfrom
, size_tto
);
const pure nothrow @property @safe size_tlength
();
templateopDollar
(size_t dim : 0)
inout pure nothrow ref @safe inout(T)opIndex
(size_tindex
); - 範囲インターフェース関数。
- auto
opSlice
(this This)(); - Nullable を範囲に変換する。
- 含まれる型が の場合でも動作する。immutable
Nullable
(T, T nullValue);
auto
nullable
(alias nullValue, T)(T t
)if (is(typeof(nullValue) == T));
Nullable
!T と同じだが、ヌル状態は特定の値として定義されている。
と同じである。例えば、Nullable!(uint, uint.max) は「」である。
uint であり、ヌル状態を表す値としてuint.max を設定する。
Nullable!(T, nullValue) は、余分なbool を格納する必要がないため、Nullable!T よりもストレージ効率が高い。T | Nullableがヌル値を提供するラップ型。 |
nullValue | のヌル状態を示すヌル値である。
Nullable .T 型でなければならない。 |
Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) { //Find the needle, returning -1 if not found return Nullable!(size_t, size_t.max).init; } void sendLunchInvite(string name) { } //Cよりは安全だ... auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; auto pos = indexOf(coworkers, "Bob"); if (!pos.isNull) { //ボブにランチの招待状を送る sendLunchInvite(coworkers[pos]); } else { //ボブが見つからない; report the error } //そしてオーバーヘッドはない static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
import std.exception : assertThrown; Nullable!(int, int.min) a; assert(a.isNull); assertThrown!Throwable(a.get); a = 5; assert(!a.isNull); writeln(a); // 5 static assert(a.sizeof == int.sizeof);
auto a = nullable!(int.min)(8); writeln(a); // 8 a.nullify(); assert(a.isNull);
- this(T
value
); value
でthis を初期化するコンストラクタ .Parameters:T value
このNullable を初期化する値。 - const @property bool
isNull
(); - this がヌル状態かどうかをチェックする。
-
Returns:trueiff this がヌル状態、そうでなければ false。Examples:
Nullable!(int, -1) ni; //"null"状態に初期化される assert(ni.isNull); ni = 0; assert(!ni.isNull);
- void
nullify
()(); - this を強制的にヌル状態にする。
- Examples:
Nullable!(int, -1) ni = 0; assert(!ni.isNull); ni = -1; assert(ni.isNull);
- void
opAssign
()(Tvalue
); value
を内部保持状態に割り当てる 。- 割り当てが成功すると this が非NULLになる。ヌルチェックは行われない。注釈: 代入が成功すると、this はヌル状態になる。Parameters:
T value
このNullable に代入するT 型の値。 それがnullvalue の場合、この の内部状態はnullに設定される。 Nullable の 内部状態はヌルに設定される。Examples:られていない場合、この の内部状態はnullに設定される。この が、すでにnull値を持つ型(ポインタなど)をラップしており、そのnull値がこの に与えNullable(ポインタなど)をラップし、そのヌル値が にヌル値が与えられていない場合、ヌル値をこの にnull値を代入しても、 、 にNULL値が与えられていない場合、これにNULL値を代入することは、他の型の値を代入するのと変わらない。これは非常に奇妙なコードになる。 の"組み込み"のヌル値を使うことで、これを避けることを強く推奨する。 の"内蔵"値である を使用することを強く推奨する。 nullValue Nullable T T nullValue//Passes enum nullVal = cast(int*) 0xCAFEBABE; Nullable!(int*, nullVal) npi; assert(npi.isNull); //Passes?! npi = null; assert(!npi.isNull);
- inout @property ref inout(T)
get
(); - this がヌル状態であってはならない。 この関数は、 への暗黙の変換の際にも呼び出される。T
事前条件 は でなければならない。 isNull false
Returns:。この が内部的に保持する値NullableExamples:import std.exception : assertThrown, assertNotThrown; Nullable!(int, -1) ni; //`get`は暗黙的に呼び出される。非リリースモードでは //エラーをスローする assertThrown!Throwable(ni == 0); ni = 0; assertNotThrown!Throwable(ni == 0);
apply
(alias fun)apply
はNullable に含まれる値をアンパックapply
はNULL自身を返す。
T t | a |
fun | nullableの内容を操作する関数である。 |
alias toFloat = i => cast(float) i; Nullable!int sample; // apply(null)は関数の戻り値の型をnullの`Nullable`にする。 Nullable!float f = sample.apply!toFloat; assert(sample.isNull && f.isNull); sample = 3; // apply(non-null)は関数を呼び出し、その結果を`Nullable`で囲む。 f = sample.apply!toFloat; assert(!sample.isNull && !f.isNull); writeln(f.get); // 3.0f
alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init; Nullable!int sample; // 関数が既に`Nullable`を返している場合、その`Nullable`はラップされない。 auto result = sample.apply!greaterThree; assert(sample.isNull && result.isNull); // 関数は`Nullable`を返すことにしてもよい。 sample = 3; result = sample.apply!greaterThree; assert(!sample.isNull && result.isNull); // あるいは、既に`Nullable`でラップされた値を返すかもしれない。 sample = 4; result = sample.apply!greaterThree; assert(!sample.isNull && !result.isNull); writeln(result.get); // 4
NullableRef
(T);
auto
nullableRef
(T)(T* t
);
NullableRef
!T は (つまり )へのポインタを格納するだけである。
T Nullable!T.sizeof == (T*).sizeofへのポインタを格納するだけである。import std.exception : assertThrown; int x = 5, y = 7; auto a = nullableRef(&x); assert(!a.isNull); writeln(a); // 5 writeln(x); // 5 a = 42; writeln(x); // 42 assert(!a.isNull); writeln(a); // 42 a.nullify(); writeln(x); // 42 assert(a.isNull); assertThrown!Throwable(a.get); assertThrown!Throwable(a = 71); a.bind(&y); writeln(a); // 7 y = 135; writeln(a); // 135
- pure nothrow @safe this(T*
value
); - コンストラクタはthis を
value
にバインドする 。 -
Parameters:
T* value
バインドする値 。 - pure nothrow @safe void
bind
(T*value
); value
に内部状態をバインド する。-
Parameters:
T* value
このNullableRef をバインドするT 型の値へのポインタ。 Examples:NullableRef!int nr = new int(42); writeln(nr); // 42 int* n = new int(1); nr.bind(n); writeln(nr); // 1
- const pure nothrow @property @safe bool
isNull
(); - this がヌル状態の場合のみ、true を返す。
-
Returns:返す。this がヌル状態の場合は真、そうでない場合は偽をExamples:
NullableRef!int nr; assert(nr.isNull); int* n = new int(42); nr.bind(n); assert(!nr.isNull && nr == 42);
- pure nothrow @safe void
nullify
(); - this を強制的にヌル状態にする。
- Examples:
NullableRef!int nr = new int(42); assert(!nr.isNull); nr.nullify(); assert(nr.isNull);
- void
opAssign
()(Tvalue
)
if (isAssignable!T); value
を内部保持状態に割り当てる 。-
Parameters:
T value
このNullableRef に割り当てるT 型の値。 このNullableRef の内部状態が初期化されていない場合 この の内部状態が初期化されていない場合、非リリース・モードではエラーがスローされる。 エラーが投げられる。 Examples:import std.exception : assertThrown, assertNotThrown; NullableRef!int nr; assert(nr.isNull); assertThrown!Throwable(nr = 42); nr.bind(new int(0)); assert(!nr.isNull); assertNotThrown!Throwable(nr = 42); writeln(nr); // 42
- inout pure nothrow @property ref @safe inout(T)
get
(); - this がヌル状態であってはならない。 この関数は、T への暗黙の変換の際にも呼び出される。Examples:
import std.exception : assertThrown, assertNotThrown; NullableRef!int nr; //`get`は暗黙的に呼び出される。 //非リリースモードではエラーをスローする assertThrown!Throwable(nr == 0); nr.bind(new int(0)); assertNotThrown!Throwable(nr == 0);
BlackHole
(Base)BlackHole
!Base はBase のサブクラスであり、 の抽象メンバ関数をすべてドゥ・ナッシング関数として自動的に実装する。
のサブクラスであり、Base のすべての抽象メンバ関数を何もしない関数として自動的に実装する。 自動実装された各
自動実装された関数は、何もせずに戻り値の型のデフォルト値を返すだけである。
のデフォルト値を返すだけである。Base | BlackHole 、継承する非最終クラス。 |
import std.math.traits : isNaN; static abstract class C { int m_value; this(int v) { m_value = v; } int value() @property { return m_value; } abstract real realValue() @property; abstract void doSomething(); } auto c = new BlackHole!C(42); writeln(c.value); // 42 // NaNであるreal.initを返す assert(c.realValue.isNaN); // 抽象関数は何もしないものとして実装される c.doSomething();
WhiteHole
(Base)WhiteHole
!Base は、Base のサブクラスである。
のサブクラスであり、抽象メンバ関数を常に失敗する関数として自動的に実装する。これらの関数は単に。
Error Whitehole は、実装されていないクラス・メンバ関数の使用をトラップするのに便利である。
実装されていないクラス・メンバ関数の使用をトラップするのに便利である。Base | WhiteHole 、継承する非最終クラス。 |
import std.exception : assertThrown; static class C { abstract void notYetImplemented(); } auto c = new WhiteHole!C; assertThrown!NotImplementedError(c.notYetImplemented()); // エラーをスローする
AutoImplement
(Base, alias how, alias what = isAbstractFunction) if (!is(how == class)): Base;
class
AutoImplement
(Interface, BaseClass, alias how, alias what = isAbstractFunction) if (is(Interface == interface) && is(BaseClass == class)): BaseClass, Interface;
AutoImplement
クラスまたはインターフェイス のすべての抽象メンバ関数を、指定された方法で自動的に(デフォルトで)実装する。
Base 、指定された方法で自動的に実装される。AutoImplement
。
Interface BaseClassを自動的に実装する。
how | テンプレート化された関数" は、関数がどのように実装/上書きさ | れるかを指定する。
how Base 、2つの引数が渡される。
実装された関数へのエイリアスである。 そしてhow 、実装された関数本体を文字列として返さなければならない。
関数本体を文字列として返さなければならない。
生成された関数本体には、以下のキーワードを使用することができる:
// オーバーライドされた関数を呼び出すたびにログメッセージを表示する。 string generateLogger(C, alias fun)() @property { import std.traits; enum qname = C.stringof ~ "." ~ __traits(identifier, fun); string stmt; stmt ~= q{ struct Importer { import std.stdio; } }; stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`; static if (!__traits(isAbstractFunction, fun)) { static if (is(ReturnType!fun == void)) stmt ~= q{ parent(args); }; else stmt ~= q{ auto r = parent(args); Importer.writeln("--> ", r); return r; }; } return stmt; } |
what | テンプレート化された関数" は、どの関数を実装/オーバーライドすべきかを決定 | する。
を決定するテンプレートである。
引数はwhat に渡される。
Base 関数へのエイリアスである。 what はブール値を返さなければならない。
渡された関数が実装/上書きされるべきことを示すために、true を返す。
を返す。
// funが何かを返すかどうかを見る。 enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void); |
注釈
生成されたコードは、std.typecons モジュールのスコープに挿入される。 したがって のスコープに挿入される。したがって、std.typecons の外の有用な関数は、生成されたコードでは使用できない。 コードでは使えない。 この問題を回避するには、import 。 generateLogger() ローカル構造体にしておく。 のテンプレートのようにする。
- コンストラクタへの可変長引数はスーパーに転送されない。
- 深いインターフェース継承は、次のようなメッセージでコンパイルエラーを引き起こす
- Error: function std.typecons.AutoImplement!(Foo).AutoImplement.bar" のようなメッセージが表示される。 は関数をオーバーライドしていません" といったメッセージが表示される。 [Bugzilla 2525]
- parent キーワードは実際にはスーパークラスの対応するメンバ関数へのデリゲート である。
- 対応するメンバ関数へのデリゲートである。 [Bugzilla 2540] でエイリアステンプレートパラメータを使用する。
- how および/またはwhat でエイリアス テンプレート パラメータを使用すると、奇妙なコンパイル エラーが発生することがある
- 奇妙なコンパイルエラーを引き起こす可能性がある。 この問題を回避するには、代わりにテンプレート・タプル・パラメータを使用する。 この問題を回避する。 [Bugzilla 4217] この問題を回避する。
interface PackageSupplier { int foo(); int bar(); } static abstract class AbstractFallbackPackageSupplier : PackageSupplier { protected PackageSupplier default_, fallback; this(PackageSupplier default_, PackageSupplier fallback) { this.default_ = default_; this.fallback = fallback; } abstract int foo(); abstract int bar(); } template fallback(T, alias func) { import std.format : format; // 実装されているすべてのメソッドについて: // - まずデフォルトを試す // - 失敗した場合のみ実行し、フォールバックを返す enum fallback = q{ try { return default_.%1$s(args); } catch (Exception) { return fallback.%1$s(args); } }.format(__traits(identifier, func)); } // 2つのクラスを組み合わせ、2番目のクラスをフォールバックとして使う alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback); class FailingPackageSupplier : PackageSupplier { int foo(){ throw new Exception("failure"); } int bar(){ return 2;} } class BackupPackageSupplier : PackageSupplier { int foo(){ return -1; } int bar(){ return -1;} } auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier()); writeln(registry.foo()); // -1 writeln(registry.bar()); // 2
generateEmptyFunction
(C, func...)enum string
generateAssertTrap
(C, func...);
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction); interface I { int foo(); string bar(); } auto i = new BlackHole!I(); // generateEmptyFunctionは、何もせずに戻り値の型のデフォルト値を返す writeln(i.foo); // 0 assert(i.bar is null);
import std.exception : assertThrown; alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap); interface I { int foo(); string bar(); } auto i = new WhiteHole!I(); // generateAssertTrapはインターフェースの未実装関数ごとに例外をスローする assertThrown!NotImplementedError(i.foo); assertThrown!NotImplementedError(i.bar);
wrap
(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))template
wrap
(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))template
unwrap
(Target) if (isMutable!Target)template
unwrap
(Target) if (!isMutable!Target)unwrap
は、wrap
によってラップされたオブジェクトを取り出すために使用できる。interface Quack { int quack(); @property int height(); } interface Flyer { @property int height(); } class Duck : Quack { int quack() { return 1; } @property int height() { return 10; } } class Human { int quack() { return 2; } @property int height() { return 20; } } Duck d1 = new Duck(); Human h1 = new Human(); interface Refleshable { int reflesh(); } // 構造適合性を持たない static assert(!__traits(compiles, d1.wrap!Refleshable)); static assert(!__traits(compiles, h1.wrap!Refleshable)); // strict upcast Quack qd = d1.wrap!Quack; assert(qd is d1); assert(qd.quack() == 1); // Duck.quackを呼び出す // strict downcast Duck d2 = qd.unwrap!Duck; assert(d2 is d1); // structural upcast Quack qh = h1.wrap!Quack; assert(qh.quack() == 2); // Human.quackを呼び出す // 構造的なダウンキャスト Human h2 = qh.unwrap!Human; assert(h2 is h1); // 構造物アップキャスト(2段階) Quack qx = h1.wrap!Quack; // Human -> Quack Flyer fx = qx.wrap!Flyer; // Quack -> Flyer assert(fx.height == 20); // Human.heightを呼び出す // 構造的ダウンキャスト(2段階) Quack qy = fx.unwrap!Quack; // Flyer -> Quack Human hy = qy.unwrap!Human; // Quack -> Human assert(hy is h1); // 構造的ダウンキャスト(1段階) Human hz = fx.unwrap!Human; // Flyer -> Human assert(hz is h1);
import std.traits : FunctionAttribute, functionAttributes; interface A { int run(); } interface B { int stop(); @property int status(); } class X { int run() { return 1; } int stop() { return 2; } @property int status() { return 3; } } auto x = new X(); auto ab = x.wrap!(A, B); A a = ab; B b = ab; writeln(a.run()); // 1 writeln(b.stop()); // 2 writeln(b.status); // 3 static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
RefCountedAutoInitialize
: int;
import core.exception : AssertError; import std.exception : assertThrown; struct Foo { int a = 42; } SafeRefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; SafeRefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto; writeln(rcAuto.refCountedPayload.a); // 42 assertThrown!AssertError(rcNoAuto.refCountedPayload); rcNoAuto.refCountedStore.ensureInitialized; writeln(rcNoAuto.refCountedPayload.a); // 42
no
- オブジェクトを自動初期化しない
yes
- オブジェクトを自動初期
- 化する
SafeRefCounted
(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));
SafeRefCounted
のインスタンスは構造体への参照である、
のインスタンスは、構造体への参照である。
構造体への参照である。 ストアには、参照カウント
とT ペイロードが含まれる。 SafeRefCounted
malloc 。
を使う。 のインスタンスがコピーされたり SafeRefCounted
のインスタンスがコピーされたり
のインスタンスがコピーされたりスコープから外れたりすると、自動的に参照カウントが増減する。
参照カウントは自動的に増減する。 参照カウントがゼロになると SafeRefCounted
free がペイロードに対してdestroy 。
を呼び出す。 T 、ペイロードがGCで割り当てられたメモリへの参照を含んでいる場合は、 。
が含まれていれば、それをGCメモリに追加する。 SafeRefCounted
がGCメモリに追加する。
に追加され、ストアに呼び出される前にGCスキャンから削除される。
free がストアで呼び出される前に、GCスキャンから削除する。
destroy の重要な結果の1つは、 ペイロードのデストラクタを呼び出すことである。
T のデストラクタを呼び出すことである。 GCで管理された参照は、デストラクタ呼び出しの間
の他のメンバは有効である。
Tの他のメンバ(ファイルハンドルやmalloc メモリへのポインタなど)は、デストラクタ呼び出し中も有効である。
の他のメンバは、デストラクタ呼び出し中も有効である。 これにより、T 。
の他のメンバ、たとえばファイルハンドルや メモリへのポインタは、デストラクタ呼び出し中も有効である。
参照カウントがゼロになった直後に、GC以外のリソースをデアロケートまたはクリーンアップすることができる。
preview=dip1000を指定しない、 SafeRefCounted
は安全ではないので
を注意深く使うべきである。ペイロードへの参照は
オブジェクトの外側で SafeRefCounted
オブジェクトの外側にエスケープしてはならない。
preview=dip1000とする、 SafeRefCounted
の場合、ペイロードへのアクセスが
を持つ borrow関数のみでアクセスされれば安全である。スコープのセマンティクスは、誤って
refCountedPayloadのエスケープを防ぐこともできる。
しかし、ペイロードの使用中に最後にカウントされた参照を破棄しないかどうかは、ユーザー次第である。そのため
refCountedPayload へのアクセスは、@system のコードでのみ可能である。
autoInit オプションは、ストアが自動的に初期化されることをオブジェクトに保証させる。
が自動的に初期化されるようにする。autoInit ==
RefCountedAutoInitialize.yes (デフォルトのオプション)のままにしておくと便利だが、ペイロードにアクセスするたびにテストが必要になる。
にすると、ペイロードにアクセスするたびにテストが必要になる。autoInit == RefCountedAutoInitialize.no の場合、ユーザーコードは
refCountedStore.isInitialized またはrefCountedStore.ensureInitialized
のいずれかを呼び出さなければならない。そうしないと、NULL
ポインタの非参照となる。
T.this() が@disable でアノテーションされている場合、コンパイルするにはautoInit が必要である。
RefCountedAutoInitialize.no でなければならない。
// `int`と`size_t`のペア - the latter being the // reference count - が動的に確保される auto rc1 = SafeRefCounted!int(5); writeln(rc1); // 5 // アロケーションは不要で、参照カウントを1つ追加するだけでよい auto rc2 = rc1; // 参照セマンティクス rc2 = 42; writeln(rc1); // 42 // rc1とrc2がスコープ外に出たとき、ペアは解放される
- struct
RefCountedStore
; - SafeRefCounted ストレージの実装である。
- const pure nothrow @nogc @property @safe bool
isInitialized
(); - 基礎となるストアが割り当てられ、初期化された場合のみ、true を返す。 を返す。
- const pure nothrow @nogc @property @safe size_t
refCount
(); - 基礎となるストアが割り当てられ、初期化されている場合は、基礎となる参照カウントを返す。 (正の整数)、そうでなければ0 を返す。
- pure nothrow @safe void
ensureInitialized
()(); - ペイロードが適切に初期化されたことを確認する。このような このような呼び出しは、通常、ペイロードを使用する前に挿入される。がアノテーションされている場合、この関数は使用できない。 T.this()アノテーションが @disable.
- inout nothrow @property ref @safe inout(RefCountedStore)
refCountedStore
(); - ストレージ実装構造体を返す。
- this(A...)(auto ref A
args
)
if (A.length > 0);
this(return scope Tval
); - ペイロードを初期化するコンストラクタ。
ポストコンディション refCountedStore.isInitialized
- void
opAssign
(typeof(this)rhs
);
voidopAssign
(Trhs
); - 代入演算子。
注意 自動初期化がオフの場合、初期化されていない SafeRefCounted に新しいペイロードを割り当てることはできない。 自動初期化がオフの場合。別のカウントされた参照を代入することはできる。
- @property ref @system T
refCountedPayload
() return;
inout pure nothrow @nogc @property ref @system inout(T)refCountedPayload
() return; - ペイロードへの参照を返す。if (autoInit ==) RefCountedAutoInitialize.yes)の場合、 refCountedStore.ensureInitialized を呼び出す。そうでない場合は、 assert(refCountedStore.isInitialized) を発行する。alias refCountedPayload this; と共に使用される。SafeRefCounted オブジェクトをT として使用できる。
最初のオーバーロードは、autoInit == RefCountedAutoInitialize.yes の場合のみ存在する。 だから、もしautoInit == RefCountedAutoInitialize.no が定数または不変オブジェクトに対して呼び出された場合は、次のようになる。 refCountedPayload
も "nothrow"となる(ただし、初期化されていない場合はアサートされる)。 (としても修飾される(ただし、初期化されていない場合はアサートされる)。
borrow
(alias fun)fun | 値または参照によってペイロードを受け入れる callable。 |
RC refCount | ペイロードへのカウントされた参照。 |
問題点 理由はまだ不明だが、この関数をUFCS構文で使用したコードは、。 を使ったコードは、@safe として推論されない。コードが明示的に @safe fun 、それを妨げるものは何もない。
auto rcInt = safeRefCounted(5); writeln(rcInt.borrow!(theInt => theInt)); // 5 auto sameInt = rcInt; writeln(sameInt.borrow!"a"); // 5 // 関数の中で`ref`を使っている auto arr = [0, 1, 2, 3, 4, 5, 6]; sameInt.borrow!(ref (x) => arr[x]) = 10; writeln(arr); // [0, 1, 2, 3, 4, 10, 6] // エイリアスを使ってペイロードを変更する sameInt.borrow!"a*=2"; writeln(rcInt.borrow!"a"); // 10
safeRefCounted
(T)(T val
);
val
.で初期化する。
T SafeRefCounted のテンプレート・パラメータは val
.
この関数は、コピー不可能な値をヒープに移動するために使用できる。
また、SafeRefCounted のautoInit オプションも無効にする。
T val |
参照カウントされる値 |
val
.
static struct File { static size_t nDestroyed; string name; @disable this(this); // コピーできない ~this() { name = null; ++nDestroyed; } } auto file = File("name"); writeln(file.name); // "name" // ファイルをコピーできず、一意の所有権を持つ static assert(!__traits(compiles, {auto file2 = file;})); writeln(File.nDestroyed); // 0 // 所有権を共有するために、ファイルを参照カウントする // 注意: // すべての`SafeRefCounted!File`ハンドルが作成され、削除される複合ステートメント(中括弧で区切られたスコープ)を書く。 // これにより、すべてのハンドルが破棄された後に何が起こるかを(スコープの後で)見ることができる。 { // `file`の内容を別の(ヒープに割り当てられた)`File`オブジェクトに移動し、 // 1つまたは複数の(最初は1つの)`SafeRefCounted!File`オブジェクト("ハンドル")を使って管理・アクセスする。 // This "moving": // (1) `file`のデストラクタを起動する(=>`File.nDestroyed`が0から1にインクリメントされ、`file.name`が`null`になる); // (2) `file`を`File.init`で上書きする(=>`file.name`が`null`になる)。 // デストラクタに`name = null;`と書くのは冗長に見えるが、 // (2)は`File`がデストラクタ(またはポストブリット演算子)を定義している場合にのみ実行され、 // `nDestroyed`インストルメンテーションがなければデストラクタを定義する理由はないことに注意してほしい。 import std.algorithm.mutation : move; auto rcFile = safeRefCounted(move(file)); writeln(rcFile.name); // "name" writeln(File.nDestroyed); // 1 writeln(file.name); // null // 同じ別の`File`オブジェクトに対して、別の`SafeRefCounted!File`ハンドルを作成する。 // いずれかのハンドルが生きている間、`File`オブジェクトは生き続ける(=>`File.nDestroyed`は変更されない)。 auto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount); // 2 writeln(File.nDestroyed); // 1 } // 最後の`SafeRefCounted!File`ハンドルが破棄されると、別の`File`オブジェクトは削除される // (i.e. つまり、上記の複合文の閉じ中括弧で、`rcFile`と`rcFile2`の両方のハンドルが破棄される: `rcFile`と`rcFile2`) // (=> `File.nDestroyed`が1から2まで再びインクリメントされる): writeln(File.nDestroyed); // 2
Proxy
(alias a)a | すべての操作のプロキシとして機能する値。l値でなければならない。 l値でなければならない。 |
struct MyInt { private int value; mixin Proxy!value; this(int n){ value = n; } } MyInt n = 10; // 元の型が持つ操作を有効にする。 ++n; writeln(n); // 11 writeln(n * 2); // 22 void func(int n) { } // 元の型への暗黙の変換を無効にする。 //int x = n; //func(n);
struct NewIntType { //Won't work; リテラル'1'はr値であり、 //l値ではない //mixin Proxy!1; //OK、nはl値である int n; mixin Proxy!n; this(int n) { this.n = n; } } NewIntType nit = 0; nit++; writeln(nit); // 1 struct NewObjectType { Object obj; //OK、objはl値である mixin Proxy!obj; this (Object o) { obj = o; } } NewObjectType not = new Object(); assert(__traits(compiles, not.toHash()));
import std.math.traits : isInfinity; float f = 1.0; assert(!f.isInfinity); struct NewFloat { float _; mixin Proxy!_; this(float f) { _ = f; } } NewFloat nf = 1.0f; assert(!nf.isInfinity);
Typedef
(T, T init = T.init, string cookie = null);
init | 新しい型の初期値(オプション)。 |
cookie | 省略可能である。 同じ原点に基づく複数の一意な型を作成するために使用される。T |
注釈 ライブラリルーチンがTypedef型を扱えない場合、、 TypedefType テンプレートを使って、Typedefがラップしている 型を取り出すことができる。
alias MyInt = Typedef!int; MyInt foo = 10; foo++; writeln(foo); // 11
alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42);
alias MyInt = Typedef!int; static void takeInt(int) {} static void takeMyInt(MyInt) {} int i; takeInt(i); // ok static assert(!__traits(compiles, takeMyInt(i))); MyInt myInt; static assert(!__traits(compiles, takeInt(myInt))); takeMyInt(myInt); // ok
alias TypeInt1 = Typedef!int; alias TypeInt2 = Typedef!int; // 2つの型定義は同じ型である。 static assert(is(TypeInt1 == TypeInt2)); alias MoneyEuros = Typedef!(float, float.init, "euros"); alias MoneyDollars = Typedef!(float, float.init, "dollars"); // 二つの型定義は同じ型ではない。 static assert(!is(MoneyEuros == MoneyDollars));
- string
toString
(this T)();
voidtoString
(this T, W)(ref Wwriter
, ref scope const FormatSpec!charfmt
)
if (isOutputRange!(W, char)); - ラップされた値を人間が読める文字列に変換する。Examples:
import std.conv : to; int i = 123; auto td = Typedef!int(i); writeln(i.to!string); // td.to!string
TypedefType
(T)import std.conv : to; alias MyInt = Typedef!int; static assert(is(TypedefType!MyInt == int)); /// 非Typedefでインスタンス化すると、その型が返される static assert(is(TypedefType!int == int)); string num = "5"; // 必要な型を取り出す MyInt myInt = MyInt( num.to!(TypedefType!MyInt) ); writeln(myInt); // 5 // 基礎となる型にキャストし、ラップされる値を得る int x = cast(TypedefType!MyInt) myInt; alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42);
scoped
(T) if (is(T == class))scoped
()の結果が
が破壊されたときに呼び出される。
スコープされたクラス・インスタンスは、子構造体のインスタンスと同じように、親のclass やstruct に埋め込むことができる、
子構造体のインスタンスと同様である。スコープされたメンバ変数は
型」でなければならない。 typeof(scoped
!Class(args))を呼び出して初期化しなければならない。
を呼び出して初期化しなければならない。例を以下に示す。
注釈 クラスのインスタンスを移動することは、たとえそのクラスへのポインタがないことが確認されていても、違法である。 クラスのインスタンスを移動することは違法である。このように、スコープされたオブジェクトを移動することは違法である。
class A { int x; this() {x = 0;} this(int i){x = i;} ~this() {} } // 標準的な使い方、スタック上にAを構築する auto a1 = scoped!A(); a1.x = 42; // `scoped`呼び出しの結果は暗黙的にクラス参照に変換される A aRef = a1; writeln(aRef.x); // 42 // スコープ付き破壊 { auto a2 = scoped!A(1); writeln(a2.x); // 1 aRef = a2; // a2はここで破壊され、Aのデストラクタが呼ばれる } // aRefは現在無効な参照である // ここでは、一時的にスコープされたAは直ちに破棄される。 // つまり、この参照は無効となる。 version (Bug) { // Wrong、`auto`を使うべきである A invalid = scoped!A(); } // 制限事項 version (Bug) { import std.algorithm.mutation : move; auto invalid = a1.move; // 不正、スコープ付きオブジェクトは移動できない } static assert(!is(typeof({ auto e1 = a1; // 不正、スコープ付きオブジェクトはコピーできない assert([a1][0].x == 42); // 同上 }))); static assert(!is(typeof({ alias ScopedObject = typeof(a1); auto e2 = ScopedObject(); // illegal、scoped!Aでビルドしなければならない auto e3 = ScopedObject(1); // 同上 }))); // エイリアスと併用する alias makeScopedA = scoped!A; auto a3 = makeScopedA(); auto a4 = makeScopedA(1); // メンバ変数として使用する struct B { typeof(scoped!A()) a; // 末尾の括弧に注意 this(int i) { // メンバーを構築する a = scoped!A(i); } } // Stack-allocate auto b1 = B(5); aRef = b1.a; writeln(aRef.x); // 5 destroy(b1); // b1.aに対してAのデストラクタを呼び出す // aRefは無効な参照となった // Heap-allocate auto b2 = new B(6); writeln(b2.a.x); // 6 destroy(*b2); // b2.aに対してAのデストラクタを呼び出す
- @system auto
scoped
(Args...)(auto ref Argsargs
); - スコープされたオブジェクトを返す。Parameters:
Args args
T のコンストラクタに渡す引数。
Flag
(string name)Flag
!"Name"の代わりにbool を使うことで、フラグの意味が呼び出し時に見えるようになる。yes/noフラグにはそれぞれ
それぞれのyes/noフラグには独自の型があり、混同や取り違えが起こらない。
例: getLine (通常はその定義から遠く離れている)を呼び出すコードは、ドキュメントを見なければ理解できない。 ドキュメントを見なければ理解できない。 APIに詳しいユーザーでも、ドキュメントを見なければ理解できない:
string getLine(bool keepTerminator) { ... if (keepTerminator) ... ... } ... auto line = getLine(false);API:逆の意味(すなわち "ignoreTerminator")を仮定し、間違ったコードを挿入すると、コンパイルと実行が誤った結果になる。 のコードはコンパイルされ、誤った結果で実行される。 booleanパラメータを
Flag
コード
getLine 。
を呼び出すコードは、APIに精通していない人でも簡単に読んで理解することができる:
string getLine(Flag!"keepTerminator" keepTerminator) { ... if (keepTerminator) ... ... } ... auto line = getLine(Yes.keepTerminator);構造体Yes とNo は、以下の略記法として用意されている。
Flag
!"Name".yesと Flag
!"Name".noの省略形として提供されており、簡潔さと
の省略形として提供されている。これらの便利な構造体は、通常、andのエイリアスを作成する必要がなく、逆効果であることを意味する。
のエイリアスを作成するのは非生産的である。 Flag
のエイリアスを作成する必要がない。
のエイリアスを作成する必要はなく、逆効果である。
構造化されていないパラメーターを用いてカテゴリーデータを渡すことは、スティーブによって「単純データ結合」に分類されている。bool
コード・コンプリート』では、スティーブ・マコーネルが「単純データ結合」に分類している。
McConnellはCode Completeの中で、他の3種類の結合とともに「単純データ結合」に分類している。
に分類されている。著者はいくつかの研究を引用して、次のように論じている。
結合はコードの品質に悪影響を及ぼす。 Flag
は
は、APIにYES/NOフラグを渡すためのシンプルな構造化手法を提供している。Flag!"abc" flag; writeln(flag); // Flag!"abc".no writeln(flag); // No.abc assert(!flag); if (flag) assert(0);
auto flag = Yes.abc; assert(flag); writeln(flag); // Yes.abc if (!flag) assert(0); if (flag) {} else assert(0);
- enum
Flag
: bool; -
no
- Flag!"Name" 型の値を作成する場合、負のオプションには Flag!"Name".no を使用する。を使用する。 Flag!"Name" 型の値を使うときは、 Flag!"Name".no と比較するか、false または0 と比較する。
yes
- Flag!"Name" 型の値を作成するとき、肯定オプションには Flag!"Name".yes を使用する。を使用する。 Flag!"Name" 。 Flag!"Name".yes と比較する。
Yes
;
struct
No
;
Yes
.encryptionの代わりに
Flag!"encryption".yes や No
.encryptionの代わりにFlag!"encryption".no 。Flag!"abc" flag; writeln(flag); // Flag!"abc".no writeln(flag); // No.abc assert(!flag); if (flag) assert(0);
auto flag = Yes.abc; assert(flag); writeln(flag); // Yes.abc if (!flag) assert(0); if (flag) {} else assert(0);
isBitFlagEnum
(E)enum A { None, A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3, } static assert(isBitFlagEnum!A);
enum B { A, B, C, D // D == 3 } static assert(!isBitFlagEnum!B);
enum C: double { A = 1 << 0, B = 1 << 1 } static assert(!isBitFlagEnum!C);
BitFlags
(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!E);
enum E { A = 1 << 0, B = 1 << 1 } E e = E.A | E.B; // SwitchErrorをスローする final switch (e) { case E.A: return; case E.B: return; }
enum Enum { A = 1 << 0, } // デフォルトで構築されたBitFlagsには値が設定されていない immutable BitFlags!Enum flags_empty; assert(!flags_empty.A); // 値は|演算子で設定できる immutable flags_A = flags_empty | Enum.A; // and tested using property access assert(flags_A.A); // or the & operator assert(flags_A & Enum.A); // which commutes. assert(Enum.A & flags_A);
enum Enum { None, A = 1 << 0, B = 1 << 1, C = 1 << 2 } immutable BitFlags!Enum flags_empty; assert(!(flags_empty & (Enum.A | Enum.B | Enum.C))); assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
enum Enum { A = 1 << 0, B = 1 << 1, C = 1 << 2, } immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B); immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C); // フラグの引き算には~演算子を使う immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A); assert(!flags_B.A && flags_B.B && !flags_B.C); // BitFlags間の交差には&を使う writeln(flags_B); // (flags_BC & flags_AB)
enum Enum { A = 1 << 0, B = 1 << 1, } BitFlags!Enum flags_empty, temp, flags_AB; flags_AB = Enum.A | Enum.B; temp |= flags_AB; writeln(temp); // (flags_empty | flags_AB) temp = flags_empty; temp |= Enum.B; writeln(temp); // (flags_empty | Enum.B) temp = flags_empty; temp &= flags_AB; writeln(temp); // (flags_empty & flags_AB) temp = flags_empty; temp &= Enum.A; writeln(temp); // (flags_empty & Enum.A)
enum Enum { A = 1 << 0, B = 1 << 1, } BitFlags!Enum flags; // 値が設定されていないBitFlagsはfalseに評価される assert(!flags); // 少なくとも1つの値が設定されているBitFlagsはtrueに評価される flags |= Enum.A; assert(flags); // これは、BitFlags間の交差をチェックするのに便利である BitFlags!Enum flags_AB = Enum.A | Enum.B; assert(flags & flags_AB); assert(flags & Enum.A); // もちろん、フラグから生の値を取り出すこともできる auto value = cast(int) flags; writeln(value); // Enum.A
enum UnsafeEnum { A = 1, B = 2, C = 4, BC = B|C } static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; })); BitFlags!(UnsafeEnum, Yes.unsafe) flags; // プロパティアクセスは、安全でない列挙型に完全に一致するかどうかをテストする flags.B = true; assert(!flags.BC); // Bのみ flags.C = true; assert(flags.BC); // BとCの両方 flags.B = false; assert(!flags.BC); // Cのみ // プロパティアクセスは、安全でない列挙型グループのすべてのビットを設定する flags = flags.init; flags.BC = true; assert(!flags.A && flags.B && flags.C); flags.A = true; flags.BC = false; assert(flags.A && !flags.B && !flags.C);
ReplaceType
(From, To, T...)ReplaceType
!(int, uint, Tuple!(int, float)[string])である。
Tuple!(uint, float)[string].置換が実行される "型
置換が実行される型は、修飾子、組み込み型コンストラクタ(ポインタ、配列、連想配列、関数、デリゲート)、テンプレートなど、任意に複雑にすることができる。
(ポインタ、配列、連想配列、関数、デリゲート)、テンプレート化された関数などである。
置換は、型定義を通過して進行する。
しかし、structやclassのメンバ型は置換されない。
なぜなら、置換後の型を表現する方法がないからである。
ReplaceType
へのエイリアスである。
置換される。static assert( is(ReplaceType!(int, string, int[]) == string[]) && is(ReplaceType!(int, string, int[int]) == string[string]) && is(ReplaceType!(int, string, const(int)[]) == const(string)[]) && is(ReplaceType!(int, string, Tuple!(int[], float)) == Tuple!(string[], float)) );
ReplaceTypeUnless
(alias pred, From, To, T...)import std.traits : isArray; static assert( is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) && is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) && is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[])) == Tuple!(string, int[])) );
Ternary
;
Ternary
.yesfortrueTernary
.noに対してfalseTernary
.unknown未知の状態として
Ternary a; writeln(a); // Ternary.unknown writeln(~Ternary.yes); // Ternary.no writeln(~Ternary.no); // Ternary.yes writeln(~Ternary.unknown); // Ternary.unknown
- enum Ternary
no
;
enum Ternaryyes
;
enum Ternaryunknown
; - の可能な状態Ternary
- pure nothrow @nogc @safe this(bool
b
);
pure nothrow @nogc @safe voidopAssign
(boolb
); - bool no を に、 を に受け取る。 を受け取る 。false yes true
- pure nothrow @nogc @safe this(const Ternary
b
); - 別の3項値から3項値を構成する
- Ternary
opUnary
(string s)()
if (s == "~");
TernaryopBinary
(string s)(Ternaryrhs
)
if (s == "|");
TernaryopBinary
(string s)(Ternaryrhs
)
if (s == "&");
TernaryopBinary
(string s)(Ternaryrhs
)
if (s == "^");
TernaryopBinary
(string s)(boolrhs
)
if (s == "|" || s == "&" || s == "^"); 論理演算の真理値表 a b ˜a a | b a & b a ^ b no no yes no no no no yes yes no yes no unknown unknown no unknown yes no no yes no yes yes yes yes yes no yes unknown yes unknown unknown unknown no unknown unknown no unknown unknown yes yes unknown unknown unknown unknown unknown unknown unknown
RefCounted
(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes);
auto rc1 = RefCounted!int(5); writeln(rc1); // 5 auto rc2 = rc1; rc2 = 42; writeln(rc1); // 42
refCounted
(T)(T val
);
static struct File { static size_t nDestroyed; string name; @disable this(this); // コピー不可 ~this() { name = null; ++nDestroyed; } } auto file = File("name"); writeln(file.name); // "name" static assert(!__traits(compiles, {auto file2 = file;})); writeln(File.nDestroyed); // 0 { import std.algorithm.mutation : move; auto rcFile = refCounted(move(file)); writeln(rcFile.name); // "name" writeln(File.nDestroyed); // 1 writeln(file.name); // null auto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount); // 2 writeln(File.nDestroyed); // 1 } writeln(File.nDestroyed); // 2
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.108.0
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku