2014年3月2日日曜日

SFINAEの書き方3種類

SFINAE (Substitution Failure Is Not An Error)でSubstitutionを行う場所は関数のsignatureならどこでも使えるので、以下のように3種類(template引数、関数引数、戻り値)の書き方が可能です。

#include <iostream>
#include <type_traits>
#include <string>

// 書き方1 (template引数)
template <typename T, class = typename std::enable_if<std::is_fundamental<T>::value>::type>
void f(T t) {
    std::cout << "fundamental type : " << t << std::endl;
}

template <typename T, class = typename std::enable_if<!std::is_fundamental<T>::value>::type>
void f(const T& t) {
    std::cout << "not fundamental type : " << t << std::endl;
}

// 書き方2 (関数引数)
template <typename T>
void g(T t, typename std::enable_if<std::is_fundamental<T>::value>::type* = 0) {
    std::cout << "fundamental type : " << t << std::endl;
}

template <typename T>
void g(const T& t, typename std::enable_if<!std::is_fundamental<T>::value>::type* = 0) {
    std::cout << "not fundamental type : " << t << std::endl;
}

// 書き方3 (戻り値)
template <typename T>
typename std::enable_if<std::is_fundamental<T>::value, void>::type h(T t) {
    std::cout << "fundamental type : " << t << std::endl;
}

template <typename T>
typename std::enable_if<!std::is_fundamental<T>::value, void>::type h(const T& t) {
    std::cout << "not fundamental type : " << t << std::endl;
}

int main() {
    int a = 0;
    std::string str = "string";
    f(a); // fundamental type : 0
    f(str); // not fundamental type : string
    g(a); // fundamental type : 0
    g(str); // not fundamental type : string
    h(a); // fundamental type : 0
    h(str); // not fundamental type : string
}

いずれも、std::enable_if::typeは存在、std::enable_if::typeは存在しない(falseの時に特殊化されたenable_if内でtypeが定義されていない)ことをSFINAEに利用しています。

1番目のtemplate引数に与える方法では、無名のクラスにデフォルトtemplate引数を与えています。
2番目の引数に与える方法では、enable_ifのtypeのポインター型にヌルポインターのデフォルト引数を与えています。
3番目の戻り値を使う方法では、typename std::enable_if::typeがT型であることを利用しています。

どの書き方もWebや書籍で見たことがあるので、どれを使用するかは好みでいいのだと思います。

0 件のコメント:

コメントを投稿