Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

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

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

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

MixinIdentifier:
    Identifier

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

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

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はパラメタ化することができます:
mixin template Foo(T) {
  T x = 5;
}

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

class Bar {
  mixin Foo;
}

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

void test() {
  Bar b = new Bar();
  b.func();      // calls Foo.func()

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

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

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

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

mixin 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が同じスコープへ置かれて、 しかもおのおのが同名の宣言を行っている場合、 その宣言を参照すると曖昧さのためエラーになります:
mixin template Foo() {
  int x = 5;
  void func(int x) { }
}

mixin 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;

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 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で宣言された関数をオーバーロードすることは可能です:

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

mixin 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;

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

mixin Foo;

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