Help us understand the problem. What is going on with this article?

なぜD言語にはSFINAEが無いのか

More than 5 years have passed since last update.

D言語 Advent Calendar 2013の七日目の記事です。

なんと、D言語にはテンプレートがある一方、 SFINAEがありません!! これについて、多くのC++erの方々が疑問に思うはずです。では、C++でSFINAEを使って実現できることは、D言語ではどのように実現されているのでしょう?

__traits(compiles)

D言語のTraitには、compilesなる命令があります。これは、 意味的に正しい(コンパイルが通る)シンボルや型や式が渡された時、trueを返す というものです。文を渡したい時はラムダ式が、宣言を渡したいときには無名クラスnew式が、それぞれイディオム的に使われます。

invalid.d
pragma(msg, __traits(compiles, hoge));
pragma(msg, __traits(compiles, 3[1]));
pragma(msg, __traits(compiles, { const int i = 1; i = 2; }));
pragma(msg, __traits(compiles, new class{ void f(){ return 0; } }()));
pragma(msg, __traits(compiles, new class{ void f(){} void f(){} }()));

pragma(msg)は、コンパイル時にメッセージを出力するものです。

shell
$ dmd -main invalid.d
false
false
false
false
false

正しくないコードが渡されると、falseになっていることが分かります。

テンプレートの制約

テンプレートには、制約と呼ばれる式を付けることが出来ます。制約がtrueになる時のみ、そのテンプレートにマッチします。

constrait.d
import std.stdio;

template abs(int i) if(i >= 0)
{
    enum abs = i;
}

template abs(int i) if(i < 0)
{
    enum abs = -i;
}

void main()
{
    writeln(abs!(-1));
    writeln(abs!(1));
}

D言語のテンプレートは、C++で例えるとテンプレート名前空間なので、気をつけてください。さらに、テンプレート内部でテンプレート名と同名のシンボルがあった場合、そのシンボルの指定を省略する必要があります。上のコードの場合、本来はabs!(-1).absと書く場所を、abs!(-1)と省略する必要があるというわけです。

shell
$ rdmd constraint.d
1
1

ちゃんとマッチするテンプレートを制御できています。

組み合わせる

上の2つの機能を組み合わせると、C++のSFINAEで実現していることが、D言語でも実現できます。例として、引数なしで呼べる関数が渡されたらそれを呼び、そうでなければ適当になにかをするテンプレート関数を作ってみます。

call.d
import std.stdio;

template call(alias func) if(__traits(compiles, func()))
{
    void call()
    {
        func();
    }
}

template call(alias func) if(!__traits(compiles, func()))
{
    void call()
    {
        writeln("cannot call");
    }
}

void func1(int i)
{
    writeln("func1");
}

void func2()
{
    writeln("func2");
}

void main()
{
    call!(func1)();
    call!(func2)();
}

上と同様に、call!(func1).call()と書く部分をcall!(func1)()と省略して書く必要があります。

shell
$ rdmd call.d
cannot call
func2

ちゃんと動いています。

まとめ

まとめると、traits(compiles)とテンプレート制約を組み合わせて使うと、 __まさにC++のSFINAEのようなことが可能になります!!やった!!!

youxkei
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away