メンバー関数の存在を確かめる方法inD言語

D言語でも、メンバー関数の存在を確かめることができます。 なので、今回は"型Tがメンバ関数fooを持っているかどうかを確かめるテンプレート"を紹介します。

D言語でメンバー関数の存在を確かめる方法は、2つほどあります。

  • __traits(compiles, ...)を使う
  • __traits(hasMember, ...)を使う

それぞれ見てみましょう。

1. __traits(compiles, ...)を使う

この方法は、どちらかというとC++でのアプローチと似ています。 つまり、コンパイルが通るか通らないかで分岐します。 D言語にSFINAEはありませんが、代わりに括弧内のコードのコンパイルが通るならtrueを返し、そうでないならfalseを返すという構文があります。


import std.stdio;
void main(){
    immutable int i;
    __traits(compiles, i = 2).writeln(); // false
    __traits(compiles, i + 2).writeln(); // true
}

上のコードの4行目 i = 2 は、immutableな変数に値を代入しようとしています。このコードはコンパイルが通らないので、traits(compiles, ...)がfalseを返します。 5行目の i + 2 はただ単純に足しているだけなので、コンパイルが通ります。よってtraits(compiles, ...)がtrueを返します。 これを応用すると、型Tがメンバ関数fooを持っているかどうかを確かめるテンプレートを書くことができます。


template hasFoo(T){
    enum hasFoo = __traits(compiles, T.init.foo());
}
struct S{
    const void foo(){}
}
void main(){
    hasFoo!int.writeln(); // false
    hasFoo!S.writeln(); // true
}

hasFooは、T.init で型Tのインスタンスを作ってfooを呼び、そのコードのコンパイルが通るか通らないかを返すテンプレートとなっています。

ちなみに、以前の記事で書かれていた、C++でのコードと今回のコードを比べると、判定部分のコード量は、

と、大きく差があります。ただ、文字数だけを比べてもナンセンスですね。

2. __traits(hasMember, ...)を使う

このアプローチは、上記の方法よりも直接的です。 D言語には、型Tが指定されたメンバを持っているかどうか調べるための構文があります。


import std.stdio;
void main(){
    __traits(hasMember, int, "init").writeln(); // true
    __traits(hasMember, int, "ini").writeln(); // false
}

intはinitプロパティを持っているので traits(hasMember, int, "init") はtrueを返します。 同様に、iniプロパティは持っていないので、traits(hasMember, int, "init") はfalseを返します。 これを応用すると、(応用するまでもない?)上と同じように型Tがメンバ関数fooを持っているかどうかを確かめるテンプレートを書くことができます。


template hasFoo(T){
    enum hasFoo = __traits(hasMember, T, "foo");
}
struct S{
    const void foo(){}
}
void main(){
    hasFoo!int.writeln(); // false
    hasFoo!S.writeln(); // true
}

1の__traits(compiles, ...)を使ったコードと、あまり変わりませんね。 ただ、この方法には短所があります。それは、fooが変数であってもtrueが返ってくる点です。 さらに、fooが関数であっても、fooの引数に関係なくtrueが返ってきます。 これらは、逆に長所と見ることもできます。 このアプローチは、引数は何でも良く、ただfooがあるかないかだけを判定するのに有用だと思われます。

まとめ

今回は traits(compiles, ...) と traits(hasMember, ...) を使う方法を紹介しました。 hasMemberについてはこれ以上応用する幅がありませんが、compilesについては更に色々と応用できます。 正直なところ、traits(compiles, ...) は最強です。 みなさんも、今回の例以外に、ぜひ traits(compiles, ...) を使ってみてください。