is式

is式についてまとめます。
Examples:

正当性の検証

// `is( Type )`でTypeが正当な型であるかを検査します。

// 通常の型に対しては`true`を返します。
static assert(is(int));
static assert(is(void));
static assert(is(string[]));

// 存在しない型に対しては`false`を返します。
static assert(!is(Int));

// まだ定義されていない型にも`false`を返します。
static assert(!is(Foo));
struct Foo {}
static assert( is(Foo));

// 関数は型として正当ですが、関数の配列は不正な型です。
alias func = int(int);
static assert( is(func));
static assert(!is(func[]));

// is式は構文的に正しいものしか検査できません。
// static assert(!is(*int));
// static assert(!is([][]));

// `is(typeof({ statement; }))`でstatementの正当性を検査できます。
static assert( is(typeof({ int x = 3; })));
static assert(!is(typeof({ int x = null; })));

// これは`__traits(compiles, { statement })`に近い動作となります。
static assert( __traits(compiles, { int x = 3; }));
static assert(!__traits(compiles, { int x = null; }));

// `is (Type identifier)`で`Type`の正当性を検証した後、正当であった場合には`identifier`が`Type`のaliasとして機能します。
// これは`static if`と組み合わせるのが有効です。
alias T = ulong;
static if (is(T[] TArray)) {
    alias A = TArray;
} else {
    alias A = void[];
}
// `A`は、`T[]`が正当な型であった場合には`T[]`を、そうでなければ`void[]`を表します。
Examples:

暗黙の型変換可能かどうかの検証

// `is(Src : Dst)`で`Src`が`Dst`に暗黙の型変換可能であるかどうかを検査します。
static assert( is(int : double));
static assert(!is(double : int));
static assert( is(char : char));

// `alias`に対しても有効です。
alias Foo = float;
static assert(is(byte : Foo));

// `alias this`に対しても有効です。
struct Bar {
    int x;
    alias x this;
}
static assert(is(Bar : int));

// `is(Src identifier : Dst)`で検査結果が`true`だった場合に`identifier`が`Dst`を示すようになります。
alias T = ulong*;
static if (is(T L : L*)) {
    // `T`がポインタ型であったとき、`L`はポインタを外した型として宣言されます。
}

// `is(Src : Dst, parameters)`で`Src`や`Dst`のためのtemplateパラメータをつけられます。
struct MyStruct(Type) {
    Type[] mem;
    alias mem this;
}
alias IntStruct = MyStruct!int;
static assert (is(IntStruct : T[], T));

// static if と組み合わせることであるtemplateパラメータを含む型からパラメータを抽出することができます。
static if (is(IntStruct : S[], S)) {
    // `IntStruct`がなんらかの配列型に暗黙の型変換可能である場合、その要素型として`S`が宣言されます。
}

// `is(Src identifier : Dst, parameters)`でそれらを組み合わせることができます。
static if (is(IntStruct UArray : U[], U)) {
    // ここで`UArray`は`int[]`を表します。
}
Examples:

型の分類

import std.typecons;
import std.traits;

// `is(Type == Type2)`で`Type`が`Type2`と等しいかどうかを検証します。
static assert( is(int == int));
static assert(!is(int == const int));

// `is(Type != Type2)`は使えないので注意が必要です。
// static assert( is(int != double));

// `alias`に対しても有効です。
alias Foo = string;
static assert(is(Foo == string));

// `is(Type == Keyword)`で`Type`が特定の条件を満たすかどうかを検証できます。

// 構造体であるかどうかの検証には`struct`を用います。
struct MyStruct {}
static assert(is(MyStruct == struct));

// 共用体であるかどうかの検証には`union`を用います。
union MyUnion {}
static assert(is(MyUnion == union));

// その他にも`class`, `interface`, `enum`, `function`, `delegate`が使えます。
class MyClass {}
interface MyInterface {}
enum MyEnum { Member }
alias MyDelegate = void delegate();
static assert(is(MyClass == class));
static assert(is(MyInterface == interface));
static assert(is(MyEnum == enum));
static assert(is(MyDelegate == delegate));

// `function`は関数そのものを表しており、関数ポインタに対しては`false`を返します。
alias MyFunction = void();
alias MyFunctionPtr = void function();
static assert( is(MyFunction == function));
static assert(!is(MyFunctionPtr == function));

// `const`, `immutable` `shared`によって型の修飾子も判定できます。
static assert(is(const int == const));
static assert(is(immutable int == immutable));
static assert(is(shared int == shared));
static assert(is(const shared int == shared));
static assert(is(const shared int == const));

// `is(Type identifier == Keyword)`で検査後に`identifier`をaliasとして扱えます。
static if (is(const int T == const))
    static assert(is(T == const int));
else
    static assert(false);

// このとき、`Keyword`として新たに`super`, `return`, `__parameters`が使えるようになります。

// `is(Type identifier == super)`は`Type`が親クラス(インターフェース)を持つ場合`true`を返し、その際`identifier`として親クラス(インターフェース)のリストを宣言します。
interface ParentInterface {}
class ParentClass {}
class ChildClass : ParentClass, ParentInterface {}
static if (is(ChildClass S == super))
{
    static assert(is(S[0] == ParentClass));
    static assert(is(S[1] == ParentInterface));
}
else
{
    static assert(false);
}

// `is(Type identifier == return)`は`Type`が関数、関数ポインタ、delegateだったときに`true`を返し、その際`identifier`として返り値の型を宣言します。
alias F1 = void();
static if (is(F1 R == return))
    static assert(is(R == void));
else
    static assert(false);

// `is(Type identifier == __parameters)`は`Type`が関数だったときに`true`を返し、その際`identifier`としてパラメータ型のリストを宣言します。
int func(int x, double y, string z = "def") { return 100; }
static if (is(typeof(func) Ps == __parameters))
{
    // パラメータの型一覧を取得できます。
    static assert(is(Ps[0] == int));
    static assert(is(Ps[1] == double));
    static assert(is(Ps[2] == string));

    // パラメータ名一覧を取得できます。
    static assert(__traits(identifier, Ps[0..1]) == "x");
    static assert(__traits(identifier, Ps[1..2]) == "y");
    static assert(__traits(identifier, Ps[2..3]) == "z");

    // デフォルト値の情報も取得できますが、トリッキーな書き方をする必要があります。
    static assert(((Ps[2..3] p) => p[0])() == "def");

    // `ParameterDefaults`を使ったほうが楽でしょう。
    static assert(ParameterDefaults!(func)[2] == "def");
}
else
{
    static assert(false);
}

// `is(Type == TypeSpecifier, parameters)`や`is(Type identifier == TypeSpecifier, parameters)`で`Type`や`TypeSpecifier`に用いるtemplateパラメータをつけられます。
enum ColorFlags { Red = 0, Blue = 1, Green = 2}
alias Color = BitFlags!ColorFlags;
static if(is(Color == BitFlags!E, E))
    static assert(is(E == ColorFlags));
else
    static assert(false);