D 1.0   D 2.0
About Japanese Translation

Last update Tue Nov 9 13:50:06 2010

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

TemplateMixin は、 任意の種類の宣言をテンプレートから取り出し、 現在のスコープへ導入します。
TemplateMixinDeclaration:
	 template TemplateIdentifier ( TemplateParameterList ) 
		{ DeclDefs }

TemplateMixin:
	mixin TemplateIdentifier ;
	mixin TemplateIdentifier MixinIdentifier ;
	mixin TemplateIdentifier !( TemplateArgumentList ) ;
	mixin TemplateIdentifier !( TemplateArgumentList ) MixinIdentifier ;

MixinIdentifier:
	Identifier

TemplateMixin は、モジュール、クラス、構造体、共用体の宣言リストの途中や、 あるいは一つの文としての位置に書くことができます。 TemplateIdentifier はテンプレートの宣言を参照します。 もしテンプレートが引数を取らないのであれば、 !(TemplateArgumentList) を省略した形式で書いても構いません。

テンプレートのインスタンス化とは異なり、テンプレートのmixinの本体は、 テンプレート定義のあるスコープではなく、mixinされた先のスコープの中で評価されます。 これは、テンプレートの本体をカット&ペーストして mixin したい場所に貼り付けるのと似た動作と言えるでしょう。 この機能は、パラメタ抽象された'鋳型'コードを挿入するのにはもちろん、 ただのインスタンス化では不可能な、 テンプレートネスト関数の作成にも役立ちます。

template Foo()
{
    int x = 5;
}

mixin Foo;

struct Bar
{
    mixin Foo;
}

void test()
{
    writefln("x = %d", x);		// 5 を表示
    {   Bar b;
	int x = 3;

	writefln("b.x = %d", b.x);	// 5 を表示
	writefln("x = %d", x);		// 3 を表示
	{
	    mixin Foo;
	    writefln("x = %d", x);	// 5 を表示
	    x = 4;
	    writefln("x = %d", x);	// 4 を表示
	}
	writefln("x = %d", x);		// 3 を表示
    }
    writefln("x = %d", x);		// 5 を表示
}
Mixinはパラメタ化することができます:
template Foo(T)
{
    T x = 5;
}

mixin Foo!(int);		// int型変数xを作成
クラスに仮想関数を付け加えることも可能です:
template Foo()
{
    void func() { writefln("Foo.func()"); }
}

class Bar
{
    mixin Foo;
}

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

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

    b = new Code();
    b.func();		// Code.func() を呼び出す
}
Mixin は、テンプレート宣言のあるスコープではなく、 mixin が現れたスコープにおいて評価されます:
int y = 3;

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

void test()
{
    int y = 8;
    mixin Foo;	// ローカルの y が選ばれる。グローバルの y ではなく。
    assert(abc() == 8);
}
alias引数を使うと、シンボルをパラメタに取ることができます:
template Foo(alias b)
{
    int abc() { return b; }
}

void test()
{
    int y = 8;
    mixin Foo!(y);
    assert(abc() == 8);
}
以下の例は、任意の文に対して使える汎用の"ダフのデバイス"を実装するのに Mixin を使っています。(この例の場合、任意の文にできる箇所を太字で表記しています。) ネストした関数もdelegateリテラルで同様に生成することができますし、 これらはコンパイラによるインライン化が見込めます:
template duffs_device(alias id1, alias id2, alias s)
{
    void duff_loop()
    {
	if (id1 < id2)
	{
	    typeof(id1) n = (id2 - id1 + 7) / 8;
	    switch ((id2 - id1) % 8)
	    {
		case 0:        do {  s();
		case 7:              s();
		case 6:              s();
		case 5:              s();
		case 4:              s();
		case 3:              s();
		case 2:              s();
		case 1:              s();
				  } while (--n > 0);
	    }
	}
    }
}

void foo() { writefln("foo"); }

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

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

ミックスインのスコープ

Mixinの中の宣言は、周囲のスコープに 'import' されます。 Mixinの中の宣言と同じ名前の宣言が周囲のスコープに存在した場合、 周囲のスコープの宣言が、 Mixinのそれを上書きします:
int x = 3;

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

mixin Foo;
int y = 3;

void test()
{
    writefln("x = %d", x);	// 3を表示
    writefln("y = %d", y);	// 3を表示
}
二つの異なるMixinが同じスコープへ置かれて、 しかもおのおのが同名の宣言を行っている場合、 その宣言を参照すると曖昧さのためエラーになります:
template Foo()
{
    int x = 5;
    void func(int x) { }
}

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

mixin Foo;
mixin Bar;

void test()
{
    writefln("x = %d", x);	// エラー、xは曖昧
    func();		// エラー、func は曖昧
}

この func() の呼び出しが曖昧とされるのは、 Foo.func と Bar.func が異なるスコープにあるからです。

mixin宣言に MixinIdentifier が指定されていれば、 それを使って曖昧さを解消できます:

int x = 6;

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

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

mixin Foo F;
mixin Bar B;

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

alias宣言を使って、 違うmixinで宣言された関数をオーバーロードすることは可能です:

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

template Bar()
{
    void func() {  }
}

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

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

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

宣言は周囲のもので上書きされますが、 mixinの内部では、それ自身の独自のスコープを持ちます。

int x = 4;

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

mixin Foo;

void test()
{
    writefln("x = %d", x);		// 4 を表示
    writefln("bar() = %d", bar());	// 5 を表示
}