英語版
このページの英語版を見る

std.variant

このモジュールは、 区別された共用体 型(別名 タグ付き共用体代数型)を実装する。 このような型は、 型が統一されたバイナリインターフェース、スクリプト言語とのインターフェース、 快適な探索的プログラミングに
オブジェクトは Variantオブジェクトは、非常に限られた制限(shared 型やコピー不可型など)のみで、あらゆる型の値を保持できる。 値の設定は、Variant オブジェクトへの代入と同様に即座に行われる。適切な型T の値を読み取るには、 getメソッドを使用する。Variant が現在T 型の値を保持しているかどうかを照会するには、 peek。現在保持されている正確な型を取得するには、 type、これは現在の値のTypeInfo を返す。
に加えて、 Variantこのモジュールでは、型コンストラクタも定義している Algebraic 型コンストラクタも定義している。Variant とは異なり、Algebraic は有限の型セットのみを許可する。 型はインスタンス化で指定される(例えば、Algebraic!(int, string)int またはstring のみを保持できる)。
警告:Algebraic は時代遅れであり、新しいコードでの使用は推奨されない。 代わりに、std.sumtype.SumType を使用すること。 "type""型

クレジット ブラッド・ロバーツによるレビュー。 ダニエル・キープによる詳細なコードレビューにより、 以下の改善が促された。 (1) 配列のサポートの改善、(2) 連想配列のサポート、 (3) ガベージコレクタに対するより親しみやすい動作。

by Brad Robertsだソース

ソースstd/variant.d

sourcesource
Examples:
Variant a; // 使用前に代入しなければならない、 さもなければ例外が発生する
// 整数で初期化する; 型をintにする
Variant b = 42;
writeln(b.type); // typeid (int)
// 値を見る
assert(b.peek!(int) !is null && *b.peek!(int) == 42);
// 言語ルールに従って自動的に変換する
auto x = b.get!(real);

// 他のタイプ、他のバリエーションを含む、任意の他のタイプを割り当てる
a = b;
a = 3.14;
writeln(a.type); // typeid (double)
// 暗黙の変換は組み込み型と同様に機能する
assert(a < b);
// 変換可能性をチェックする
assert(!a.convertsTo!(int)); // doubleはintに変換できない
// 文字列と他のすべての配列がサポートされている
a = "now I'm a string";
writeln(a); // "now I'm a string"(今、私は文字列だ)

// 配列を代入することもできる
a = new int[42];
writeln(a.length); // 42
a[5] = 7;
writeln(a[5]); // 7

// クラス値も代入できる
class Foo {}
auto foo = new Foo;
a = foo;
assert(*a.peek!(Foo) == foo); // 完全な型情報が保持される
xml-ph-0000@deepl.internalに与えられた最大のタイプを与える。
enum auto maxSize(Ts...);
sizeof に与えられた最大の型を与える。
See Also: https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1
forumforum
Examples:
struct Cat { int a, b, c; }

align(1) struct S
{
    long l;
    ubyte b;
}

align(1) struct T
{
    ubyte b;
    long l;
}

static assert(maxSize!(int, long) == 8);
static assert(maxSize!(bool, byte) == 1);
static assert(maxSize!(bool, Cat) == 12);
static assert(maxSize!(char) == 1);
static assert(maxSize!(char, short, ubyte) == 2);
static assert(maxSize!(char, long, ubyte) == 8);
import std.algorithm.comparison : max;
static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof));
static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof));
static assert(maxSize!(int, ubyte[7]) == 7);
static assert(maxSize!(int, ubyte[3]) == 4);
static assert(maxSize!(int, int, ubyte[3]) == 4);
static assert(maxSize!(void, int, ubyte[3]) == 4);
static assert(maxSize!(void) == 1);
ユーザーコードで直接使用されることはほとんどないバックエンドタイプ。 一般的に使用される2つのタイプを使用する
struct VariantN(size_t maxDataSize, AllowedTypesParam...);
バックエンド型は、ユーザーコードで直接使用されることはほとんどない。 一般的に使用される型は2つあり、 VariantN:
  1. Algebraic:限定された型ユニバースを持つ閉じた区別共用体 (例えば、Algebraic!(int, double, string) はこれら3つの型のみを受け入れ、それ以外は拒否する )。
  2. Variant:無制限の型集合を許容する開放型共用体。Variantの型のうち、 最大の組み込み型よりも大きな型は自動的に ボックス化される。つまり、大きな型であっても、Variant 内のサイズは ポインタのサイズのみとなるが、これはオーバーヘッドも発生することを意味する。Variant は、すべてのプリミティブ型と すべてのユーザー定義型に対応している。
AlgebraicVariant は、 VariantN のインターフェースを共有している。(下記それぞれのドキュメントを参照)
VariantN型パラメータ化された共用体型である。 格納される型の最大サイズ(maxDataSize )と 許可された型のリスト(AllowedTypes )で型パラメータ化される。 リストが空の場合、 maxDataSize (整列のために切り上げ)までのサイズの型は、 VariantNオブジェクトに格納できます(これより大きな型は ボックス化されます)。
Examples:
alias Var = VariantN!(maxSize!(int, double, string));

Var a; // 使用前に代入しなければならない
// 整数で初期化する; 型をintにする
Var b = 42;
writeln(b.type); // typeid (int)
// 値を覗き見る
assert(b.peek!(int) !is null && *b.peek!(int) == 42);
// 言語ルールに従って自動的に変換する
auto x = b.get!(real);

// 他のタイプ、他のバリエーションを含む、任意の他のタイプを割り当てる
a = b;
a = 3.14;
writeln(a.type); // typeid (double)
// 暗黙の変換は組み込み型と同様に機能する
assert(a < b);
// 変換可能性をチェックする
assert(!a.convertsTo!(int)); // doubleはintに変換できない
// 文字列と他のすべての配列がサポートされている
a = "now I'm a string";
writeln(a); // "now I'm a string"(今、私は文字列だ)
Examples: 配列を割り当てることもできる
配列を割り当てることもできる
alias Var = VariantN!(maxSize!(int[]));

Var a = new int[42];
writeln(a.length); // 42
a[5] = 7;
writeln(a[5]); // 7
objectarray
Examples: クラス値も割り当て可能
クラス値も割り当て可能
alias Var = VariantN!(maxSize!(int*)); // クラスはポインタである
Var a;

class Foo {}
auto foo = new Foo;
a = foo;
assert(*a.peek!(Foo) == foo); // 完全な型情報が保持される
alsoも
alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam);
許可された型のリスト。空の場合は、すべての型が許可される。
enum bool allowed(T);
TVariantN オブジェクト内に静的に保存できるかどうかを、TAllowedTypes で検索して 判断する。
this(T)(T value);
汎用型の引数からVariantN 値を構築する。 許可されていない型を静的に拒否する。
this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value)
if (!is(T : VariantN) && (Types.length > 0) && allSatisfy!(allowed, Types));
代入を許可する部分代数型
VariantN opAssign(T)(T rhs);
ジェネリック引数からVariantN を代入する。 許可されていない型を静的に拒否する。
const pure nothrow @property bool hasValue();
VariantN オブジェクトが 有効な値(有効な値で初期化されているか、または有効な値から代入されている )を保持している場合にのみ、trueを返す。
Examples:
Variant a;
assert(!a.hasValue);
Variant b;
a = b;
assert(!a.hasValue); // まだ値がない
a = 5;
assert(a.hasValue);
inout @property inout(T)* peek(T)();
VariantN オブジェクトが正確な型 T値を保持している場合は、 その値へのポインタを返す。 それ以外の場合は、null を返す。T が静的に許可されていない場合、 peek はコンパイルされない。
emailemail
Examples:
Variant a = 5;
auto b = a.peek!(int);
assert(b !is null);
*b = 6;
writeln(a); // 6
const nothrow @property @trusted TypeInfo type();
現在保持されている値のtypeid を返す。
const @property bool convertsTo(T)();
VariantNオブジェクトが暗黙的にT 型に変換可能なオブジェクトを保持している場合にのみ、true を返す。 暗黙的な変換可能性は、AllImplicitConversionTargets に従って定義される。 オブジェクトに格納されている値を、 必要な型または許可された型のリスト内のインデックスを指定して返す。後者のオーバーロードは、 バインドされたバリアントのみに適用される(
inout @property inout(T) get(T)();

inout @property auto get(uint index)()
if (index < AllowedTypes.length);
VariantN オブジェクトに格納されている値を返す。必要な型を指定するか、 または許可された型のリスト内のインデックスを指定する。後者のオーバーロードは、 バインドされたバリアント(例えば、 Algebraic
Parameters: 要求された型。現在格納されている値は、暗黙的に要求された型に変換できなければならない。実際にはxml-ph-0000@deepl.internal。暗黙的な変換が不可能な場合は、xml-ph例外が発生する。
T 要求された型。現在格納されている値は、暗黙的に 要求された型に変換できなければならない。実際にはDecayStaticToDynamicArray!T 。 暗黙的な変換が不可能な場合は、VariantException がスローされる。
index AllowedTypesParam における型のインデックス。ゼロベース。
@property T coerce(T)();
VariantN オブジェクトに格納された値を返す。 明示的に要求された型に変換(強制)される TT が文字列型の場合、値は文字列としてフォーマットされる。 VariantN オブジェクトが文字列の場合、 文字列をT 型にパースする試みが行われる。 変換が不可能な場合は、 VariantException がスローされる。
string toString();
格納された値を文字列としてフォーマットする。
const bool opEquals(T)(auto ref T rhs)
if (allowed!T || is(immutable(T) == immutable(VariantN)));
"="および"!="演算子で使用される等価比較。
int opCmp(T)(T rhs)
if (allowed!T);
"<>"、"<>="、">"、">=" 演算子で使用される順序比較。 保持された"値"と"値"の比較が意味をなさない場合、 rhs例外がスローされます。
const nothrow @safe size_t toHash();
保持されている値のハッシュを計算する。
VariantN opBinary(string op, T)(T rhs)
if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && is(typeof(opArithmetic!(T, op)(rhs))));

VariantN opBinary(string op, T)(T rhs)
if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && is(typeof(opLogic!(T, op)(rhs))));

VariantN opBinaryRight(string op, T)(T lhs)
if ((op == "+" || op == "*") && is(typeof(opArithmetic!(T, op)(lhs))));

VariantN opBinaryRight(string op, T)(T lhs)
if ((op == "&" || op == "|" || op == "^") && is(typeof(opLogic!(T, op)(lhs))));

VariantN opBinary(string op, T)(T rhs)
if (op == "~");

VariantN opOpAssign(string op, T)(T rhs);
VariantN オブジェクトと数値値の算術演算。 すべての算術演算は、VariantNオブジェクトを返す。 このオブジェクトの型は、関与する両方の値の型に応じて決定される。 変換ルールは、Dの算術変換用の組み込みルールを模倣している。
inout inout(Variant) opIndex(K)(K i);

Variant opIndexAssign(T, N)(T value, N i);

Variant opIndexOpAssign(string op, T, N)(T value, N i);
配列および連想配列の操作。 VariantN が(連想)配列を含む場合、配列にインデックスを指定できる。 そうでない場合は、例外がスローされる。
to can be indexedインデックス化される
Examples:
Variant a = new int[10];
a[5] = 42;
writeln(a[5]); // 42
a[5] += 8;
writeln(a[5]); // 50

int[int] hash = [ 42:24 ];
a = hash;
writeln(a[42]); // 24
a[42] /= 2;
writeln(a[42]); // 12
@property size_t length();
VariantN が(連想)配列を含む場合、 その配列の長さを返す。それ以外の場合、例外がスローされる。
int opApply(Delegate)(scope Delegate dg)
if (is(Delegate == delegate));
VariantN が配列を含む場合、 dg配列の各要素に順番に適用する。 そうでない場合は、例外をスローする。
代数データ型は、可能な型からなる閉じた集合に制限される。 これはエイリアスである。
template Algebraic(T...)
代数データ型は、可能な型の閉じたセットに制限される。 これは、 VariantN適切に構築された最大サイズを持つ。 Algebraicこれは、 判別型が保持しうる内容を制限したい場合に有用である。 よりシンプルで効率的な操作を定義する際に。
警告:Algebraic は時代遅れであり、新しいコードでの使用は推奨されない。 代わりに、std.sumtype.SumType を使用すること。
xml-ph-0000@deepl.internalxml-ph-0001@deepl.internal
Examples:
auto v = Algebraic!(int, double, string)(5);
assert(v.peek!(int));
v = 3.14;
assert(v.peek!(double));
// auto x = v.peek!(long); // コンパイルされない、long型は許可されない
// v = '1'; // コンパイルされない、char型は許可されない
Examples:

自己参照型

代数データ構造の便利な一般的な用途として、自己参照データ構造の定義がある。すなわち、 自身の型の値への参照を内部に埋め込む構造である。
これは、 Algebraic定義される型への参照が必要な場合は常に、This をプレースホルダとして使用することで 実現される。 Algebraicインスタンス化 の際には、構成要素の型に対してアルファベット順の名前変更が実行され、Thisが 自己参照型に置き換えられる。This を含む型の構造は 任意に複雑にすることができる。
import std.typecons : Tuple, tuple;

// ツリーは、他の2つのツリーのleafかbranchである
alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*));
Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
Tree!int* right = tree.get!1[1];
writeln(*right); // 43

// オブジェクトはdouble、string、またはオブジェクトのハッシュである
alias Obj = Algebraic!(double, string, This[string]);
Obj obj = "hello";
writeln(obj.get!1); // "hello"
obj = 42.0;
writeln(obj.get!0); // 42
obj = ["customer": Obj("John"), "paid": Obj(23.95)];
writeln(obj.get!2["customer"]); // "John"
self-referenced自己参照
alias Variant = VariantN!32LU.VariantN;
エイリアスは、 VariantNcrealchar[]void delegate() の最大サイズでインスタンス化される。 これにより、 VariantDのすべてのプリセット型(数値型、 ポインタ、デリゲート、クラス参照など)をアンボックスした状態で格納できるだけの十分なサイズを確保します。 VariantN を直接使用し、異なる最大サイズを設定して、 より大きな型をアンボックスで格納したり、メモリを節約したりすることもできます。
class referenceクラス参照
Examples:
Variant a; // 使用前に割り当てなければならない。さもなければ例外が発生する
// 整数で初期化する; 型をintにする
Variant b = 42;
writeln(b.type); // typeid (int)
// 値を見る
assert(b.peek!(int) !is null && *b.peek!(int) == 42);
// 言語ルールに従って自動的に変換する
auto x = b.get!(real);

// 他のタイプ、他のバリエーションを含む、任意の他のタイプを割り当てる
a = b;
a = 3.14;
writeln(a.type); // typeid (double)
// 暗黙の変換は組み込み型と同様に機能する
assert(a < b);
// 変換可能性をチェックする
assert(!a.convertsTo!(int)); // doubleはintに変換できない
// 文字列と他のすべての配列がサポートされている
a = "now I'm a string";
writeln(a); // "now I'm a string"(今、私は文字列だ)
Examples: 配列を代入することもできます。
配列を割り当てることもできます
Variant a = new int[42];
writeln(a.length); // 42
a[5] = 7;
writeln(a[5]); // 7
xml-ph-0000@deepl.internalxml-ph-0000@deepl.internal
Examples: クラス値も割り当て可能
クラス値も割り当て可能
Variant a;

class Foo {}
auto foo = new Foo;
a = foo;
assert(*a.peek!(Foo) == foo); // 完全な型情報が保持される
alsoも
Variant[] variantArray(T...)(T args);
から構築されたバリアントの配列を返す args
これは意図的な仕様である。構築中、Variant は 保持する型の静的型情報を必要とする。これにより、 高速な取得のための関数へのポインタを保存する。
static静的
Examples:
auto a = variantArray(1, 3.14, "Hi!");
writeln(a[1]); // 3.14
auto b = Variant(a); // variant配列をバリアントとして
writeln(b[1]); // 3.14
class VariantException: object.Exception;
次の3つのケースでスローされる。
  1. 初期化されていないVariant が代入およびhasValue 以外の方法で使用された場合、
  2. 互換性のないターゲット型でget またはcoerce が試行された場合、
  3. 互換性のないタイプのVariant オブジェクト間の比較が 試みられた。
internal内部
Examples:
import std.exception : assertThrown;

Variant v;

// 初期化されていない使用
assertThrown!VariantException(v + 1);
assertThrown!VariantException(v.length);

// .getと互換性のないターゲットタイプ
assertThrown!VariantException(Variant("a").get!int);

// 互換性のない型同士の比較
assertThrown!VariantException(Variant(3) < Variant("a"));
TypeInfo source;
変換または比較におけるソース型
TypeInfo target;
変換または比較のターゲット型
template visit(Handlers...) if (Handlers.length > 0)
デリゲートまたは関数を、保持されている型に応じて指定されたものに適用し Algebraic保持されている型に応じて、 すべての型が訪問関数によって処理されるようにする。
現在保持されている値をパラメータとして持つデリゲートまたは関数は、variant の現在の値で呼び出される。訪問ハンドラは、 テンプレートパラメータリストで渡される。variant のすべての保持型が、 すべてのハンドラで処理されることが静的に保証される。 visitこれにより、デリゲートおよび静的関数をパラメータとして渡すことができる。
型指定のないパラメータを持つ関数が指定された場合、この関数は バリアントが他のどの関数にも一致しない型を含む場合に呼び出される。 これは、複数の可能な型にわたって同じ関数を適用するために使用できる。 正確に1つのジェネリック関数が許可される。
パラメータのない関数が指定された場合、この関数はvariant が値を保持していない場合に呼び出される。パラメータのない関数は、1つだけ許可される。
訪問者のいずれかで同じ型に一致する重複するオーバーロードは許可されない。
Returns:
訪問の戻り値の型は、訪問関数から推測され、 すべてのオーバーロードで同じでなければならない。
Throws: xml-ph-0000@deepl.internal が値を持たず、パラメータなしの代替関数が指定されていない場合
VariantExceptionvariant が値を保持せず、 パラメータなしの代替関数が指定されていない場合。
overloadoverload
Examples:
Algebraic!(int, string) variant;

variant = 10;
assert(variant.visit!((string s) => cast(int) s.length,
                      (int i)    => i)()
                      == 10);
variant = "string";
assert(variant.visit!((int i) => i,
                      (string s) => cast(int) s.length)()
                      == 6);

// エラー関数の使用法
Algebraic!(int, string) emptyVar;
auto rslt = emptyVar.visit!((string s) => cast(int) s.length,
                      (int i)    => i,
                      () => -1)();
writeln(rslt); // -1

// 汎用関数の使用法
Algebraic!(int, float, real) number = 2;
writeln(number.visit!(x => x += 1)); // 3

// int/float用の汎用関数で、文字列用の動作は別に用意されている
Algebraic!(int, float, string) something = 2;
assert(something.visit!((string s) => s.length, x => x) == 2); // 汎用
something = "asdf";
assert(something.visit!((string s) => s.length, x => x) == 4); // 文字列

// 汎用ハンドラと空ハンドラ
Algebraic!(int, float, real) empty2;
writeln(empty2.visit!(x => x + 1, () => -1)); // -1
auto visit(VariantType)(VariantType variant)
if (isAlgebraic!VariantType);
template tryVisit(Handlers...) if (Handlers.length > 0)
動作は同じだが、 visitが、すべての型が訪問関数で処理されることを強制するものではない。
パラメータのない関数が指定された場合、variant が値を保持していないか、または 訪問関数で処理できない型を保持している
Returns:
tryVisitの戻り値の型は、訪問関数から推測され、 すべてのオーバーロードで同じでなければならない。
Throws: xml-ph-0000@deepl.internal が値を保持していない場合、またはxml-ph-0001@deepl.internal が値を保持しており、その値が訪問関数で処理されていない場合、
VariantExceptionvariant が値を保持していない場合、またはvariant が訪問関数で処理されない値を保持している場合、 パラメータなしの代替関数が指定されていない場合、
internalinternal
Examples:
Algebraic!(int, string) variant;

variant = 10;
auto which = -1;
variant.tryVisit!((int i) { which = 0; })();
writeln(which); // 0

// エラー関数の使用法
variant = "test";
variant.tryVisit!((int i) { which = 0; },
                  ()      { which = -100; })();
writeln(which); // -100
auto tryVisit(VariantType)(VariantType variant)
if (isAlgebraic!VariantType);