Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

C++11 type_traits の原理

std::iteratorのソースを読んでいたところ、C++11のtype_traitsがよく分からないため、勉強中。

備忘録として解説しておく。

C++11で<type_traits>というヘッダがSTLに追加されている。

type_traits - cpprefjp C++日本語リファレンス

型の特性を判定、操作するための機能らしい。

<type_traits>ヘッダから、integral_constant,is_const構造体を取り出し、 とりあえず、必要ない部分をカットして簡略化し、サンプルソースにしてみた。

template <class T, T v> struct integral_constant {
    static const T value = v;
};

typedef integral_constant<bool, true>  true_type;
/*
 展開するとこうなる
 struct true_type {
   static const bool value = true; 
 };
 */

typedef integral_constant<bool, false> false_type;
/*
 展開するとこうなる
 struct false_type {
   static const bool value = false;
 };
 */

// (A)
template <class T> struct is_const : public false_type
{
};

// (B)
template <class T> struct is_const<T const> : public true_type
{
};

int main(int argc, const char * argv[]) {
    
    // SFINEにより、(A)のテンプレートクラスに展開される
    printf("%d\n", is_const<int>::value); // 0
    
    // SFINEにより、(B)のテンプレートクラスに展開される
    printf("%d\n", is_const<const int>::value); // 1
    
    return 0;
}

integral_constantはstatic constな変数を一つだけ持つ構造体だ。 is_const構造体はこの構造体を継承して作られる。

true_type, false_typeはvalueにそれぞれtrue,falseの値が入った構造体となる。

難しいのは(A),(B)の部分。 SFINEを理解するため、まず、C++での関数オーバーロードについて考える。

// (C)
int func(void) { return 1; }
// (D)
int func(int) { return 2; } 

func(); // (C)が呼び出される
func(3); // (D)が呼び出される

このようにC++には同じ名称の関数が引数に応じて呼び分けられる関数オーバーロードという機能がある。 似た仕組みで同じ名称のクラステンプレートがテンプレート引数に応じて呼び分けられる機能がSFINEと考えてよい。 ルールとしては、最も指定が一致するものから検索される。

そのため、(A),(B)は同じ名称のクラステンプレートだが、(B)の方が指定が多く、(A)は(B)を含む一般性を持つ。 型Tにconstが指定されている場合は、(B)の方が指定が一致しているのでこちらに置き換えられ、 型Tにconstが指定されていない場合は、(B)に一致しないので、(A)に置き換えられるのである。