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

テンプレートミックスイン

TemplateMixinは、TemplateDeclarationの本体から任意の宣言セットを取り出し、それを挿入する。 を受け取り、それらを現在のコンテキストに挿入する。 を現在のコンテキストに挿入する。

TemplateMixinDeclaration:
    mixin template Identifier TemplateParameters Constraintopt { DeclDefsopt }
TemplateMixin: mixin MixinTemplateName TemplateArgumentsopt Identifieropt ;
MixinTemplateName: . MixinQualifiedIdentifier MixinQualifiedIdentifier Typeof . MixinQualifiedIdentifier
MixinQualifiedIdentifier: Identifier Identifier . MixinQualifiedIdentifier TemplateInstance . MixinQualifiedIdentifier

TemplateMixinは、モジュールの宣言リストで使うことができる、 クラス、構造体、共用体、またはステートメントとして宣言される。 MixinTemplateNameTemplateDeclarationまたは TemplateMixinDeclaration を参照しなければならない。 TemplateDeclarationがパラメータを必要としない場合、TemplateArguments は省略できる。

テンプレートのインスタンス化とは異なる、 テンプレートミックスインの本体は、テンプレートのインスタンス化と違って、ミックスインが現れるスコープ内で評価される。 テンプレート宣言が定義されている場所ではなく、ミックスインが現れるスコープ内で評価される。 が定義されている場所ではない。これは、ネストしたスコープにあるmixinの場所に、テンプレートの本体をカット・アンド・ペーストすることに似ている。 テンプレート本体をネストされたスコープのミックスインの位置にカット&ペーストするのと似ている。これは パラメータ化された「定型的な」コードを注入するのに便利である。 テンプレート化されたネストされた関数を作るのにも便利だ。 テンプレートのインスタンス化では必ずしも可能ではない。

int y = 3;

mixin template Foo()
{
    int abc() { return y; }
}

void test()
{
    int y = 8;
    mixin Foo; // グローバルのyではなくローカルのyがピックアップされる
    assert(abc() == 8);
}
import std.stdio : writeln;

mixin template Foo()
{
    int x = 5;
}

mixin Foo;

struct Bar
{
    mixin Foo;
}

void main()
{
    writeln("x = ", x);         // 5を表示する
    {
        Bar b;
        int x = 3;

        writeln("b.x = ", b.x); // 5を表示する
        writeln("x = ", x);     // 3を表示する
        {
            mixin Foo;
            writeln("x = ", x); // 5を表示する
            x = 4;
            writeln("x = ", x); // 4を表示する
        }
        writeln("x = ", x);     // 3を表示する
    }
    writeln("x = ", x);         // 5を表示する
}

ミックスインのパラメーター

ミックスインは以下のことができる。 パラメータできる:

mixin template Foo(T)
{
    T x = 5;
}

mixin Foo!(int);           // int型のxを作成する

ミックスインはシンボルをパラメータ化することができる。 エイリアスパラメータを使う:

mixin template Foo(alias b)
{
    int abc() { return b; }
}

void test()
{
    int y = 8;
    mixin Foo!(y);
    assert(abc() == 8);
}

この例では、"ミックスイン"を使って、汎用のダフ・デバイスを実装している。 を実装している。 は太字)。ネストされた関数とデリゲート・リテラルが生成される。 これらはコンパイラによってインライン化される:

import std.stdio : writeln;

mixin template duffs_device(alias low, alias high, alias fun)
{
    void duff_loop()
    {
        if (low < high)
        {
            auto n = (high - low + 7) / 8;
            switch ((high - low) % 8)
            {
                case 0: do { fun(); goto case;
                case 7:      fun(); goto case;
                case 6:      fun(); goto case;
                case 5:      fun(); goto case;
                case 4:      fun(); goto case;
                case 3:      fun(); goto case;
                case 2:      fun(); goto case;
                case 1:      fun(); continue;
                default:     assert(0, "Impossible");
                        } while (--n > 0);
            }
        }
    }
}

void main()
{
    int i = 1;
    int j = 11;

    mixin duffs_device!(i, j, delegate { writeln("foo"); });
    duff_loop();  // foo()を10回実行する
}

ミックスインのスコープ

ミックスインの宣言は入れ子になったスコープに置かれ、そのスコープを囲むように インポート」される。 スコープに「インポート」される。ミックスイン内の宣言の名前が周囲のスコープにある宣言と同じ場合、その宣言は「インポート」される。 宣言の名前が周囲のスコープにある宣言と同じ場合、周囲の宣言はミックスインの宣言をオーバーライドする。 がミックスインを上書きする:

import std.stdio : writeln;

int x = 3;

mixin template Foo()
{
    int x = 5;
    int y = 5;
}

mixin Foo;
int y = 3;

void main()
{
    writeln("x = ", x);  // 3を表示する
    writeln("y = ", y);  // 3を表示する
}

たとえ宣言がオーバーライドされたとしても、ミックスインには独自のスコープがある。 ミックスインは独自のスコープを持つ:

import std.stdio : writeln;

int x = 4;

mixin template Foo()
{
    int x = 5;
    int bar() { return x; }
}

mixin Foo;

void main()
{
    writeln("x = ", x);         // 4を表示する
    writeln("bar() = ", bar()); // 5を表示する
}

2つの異なるミックスインが同じスコープに置かれ、それぞれが同じ名前の宣言を定義している場合、ミックスインは独自のスコープを持つ。 が同じ名前の宣言を定義している場合、その宣言が参照されたときに曖昧性エラーが発生する エラーが発生する:

import std.stdio : writeln;

mixin template Foo()
{
    int x = 5;
    void func(int x) { }
}

mixin template Bar()
{
    int x = 4;
    void func(long x) { }
}

mixin Foo;
mixin Bar;

void main()
{
    writeln("x = ", x);    // エラー、xがあいまい
    func(1);               // エラー、funcがあいまい
}

func()Foo.funcBar.func は異なるスコープにある。

曖昧さを解消する

ミックスインにIdentifierがある場合、それを使って曖昧性を解消することができる。 を使うことができる:

import std.stdio : writeln;

int x = 6;

mixin template Foo()
{
    int x = 5;
    int y = 7;
    void func() { }
}

mixin template Bar()
{
    int x = 4;
    void func() { }
}

mixin Foo F;
mixin Bar B;

void main()
{
    writeln("y = ", y);     // 7を表示する
    writeln("x = ", x);     // 6を表示する
    writeln("F.x = ", F.x); // 5を表示する
    writeln("B.x = ", B.x); // 4を表示する
    F.func();                  // Foo.funcを呼び出す
    B.func();                  // Bar.funcを呼び出す
}

エイリアス宣言を使って オーバーロード エイリアスの宣言は、異なるミックスインで宣言された関数のオーバーロード・セットを形成するために使用することができる:

mixin template Foo()
{
    void func(int x) {  }
}

mixin template Bar()
{
    void func(long x) {  }
}

mixin Foo!() F;
mixin Bar!() B;

alias func = F.func;
alias func = B.func;

void main()
{
    func(1);  // B.funcを呼び出す
    func(1L); // F.funcを呼び出す
}

集約型ミックスイン

ミックスインの仮想関数

ミックスインはクラスに仮想関数を追加することができる:

import std.stdio : writeln;

mixin template Foo()
{
    void func() { writeln("Foo.func()"); }
}

class Bar
{
    mixin Foo;
}

class Code : Bar
{
    override void func() { writeln("Code.func()"); }
}

void main()
{
    Bar b = new Bar();
    b.func();      // Foo.func()を呼び出す

    b = new Code();
    b.func();      // Code.func()を呼び出す
}

ミックスイン・デストラクタ

集約型は追加のデストラクタをミックスすることができる。 デストラクタは宣言順とは逆の順序で実行される。

import std.stdio;

mixin template addNewDtor()
{
    ~this()
    {
        writeln("Mixin dtor");
    }
}

struct S
{
    ~this()
    {
        writeln("Struct dtor");
    }

    mixin addNewDtor;
}

void main()
{
    S s;
    // `Mixin dtor`を表示する
    // `Struct dtor`を表示する
}