基本的な型のランダムな値を返す

## 背景

いま、基本型（e.g. `int32_t`, `uint64_t`, `double`, `float`）のどれが来ても対応できるようなジェネリックなコードを書いているとします。そして、そのコードにランダムな値を渡したいとします（例えばテストをしているとか）。

```cpp
template<typename T>
bool is_ok(T rnd);
```

ランダムな値はどう生成したらいいでしょう。
標準ライブラリの`<random>`では、整数型は`uniform_int_distribution`、浮動小数点数型は`uniform_real_distribution`で、名前が異なります。

できれば以下のようなランダムな値を返すジェネリックな関数を書きたいものですが、使うクラスの名前が違うので`int`と`double`で同じ実装は書けません。何らかの方法で実装を切り替える必要があります。

```cpp
template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
   // ...?;
}
```

## 部分特殊化・オーバーロード

関数templateは部分特殊化ができません。

```cpp
template<typename RNG>
int generate_random<int, RNG>(RNG&& rng);
template<typename RNG>
double generate_random<double, RNG>(RNG&& rng); // できない
```

ではオーバーロードではどうでしょう。

```cpp
template<typename RNG>
void generate_random(RNG&& rng, int& value);
template<typename RNG>
void generate_random(RNG&& rng, double& value);
```

これを全ての基本型について書くのはあまりにも面倒です。
そうでなくとも、使う側からすれば書き込み先を先に用意する必要があり、しかも書き込み可能にしなければならないインターフェースはそんなに使い勝手は良くないです。

```cpp
double value;
generate_random(rng, value);
// vs
const auto value = generate_random<double>(rng);
```

では、無理やり部分特殊化をできるようにするのはどうでしょう。クラステンプレートは部分特殊化できるので、クラスのメンバを呼び出すようにすれば部分特殊化が可能です。

```cpp
template<typename T>
struct generate_random_impl
{
    template<typename RNG>
    static T invoke(RNG&& rng);
};

// generate_random_implを特殊化する……

template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
    return generate_random_impl<T>::invoke(rng);
}
```

しかし、これも結局目的の型について全ての部分特殊化を用意する必要があることに変わりありません。
そもそも今回の目的は「個々の型について別の実装を（特殊化の本来の意味）」ではなく、「特定の種の型について分岐する」という話なので特殊化で解決するのはそもそも少し筋が悪いでしょう。

浮動小数点数型ならこう、整数型ならこう、というだけの話なのに、それをそのまま実装できないものでしょうか。

## タグディスパッチ

例えば、タグディスパッチという手法があります。これは、特別な構造体を作ってオーバーロード解決につかうというものです。
といっても構造体に特別な実装は必要ありません。というか実装がそもそも必要ありません。別の型であることがわかればそれで事足りるので、空でかまいません（もちろん、値を持っていても構いません。型しか見ないので）。

たとえば、`std::true_type`と`std::false_type`があります。これらは違う型なのでオーバーロード解決に使うことが出来ます。整数型かそうでないかをこれを使って分岐してみましょう。

```cpp
template<typename T, typename RNG>
T generate_random_impl(RNG&& rng, std::true_type) // 整数型ならtrue_typeを渡す
{
    std::uniform_int_distribution<T> uni(0, 100);
    return uni(rng);
}

template<typename T, typename RNG>
T generate_random_impl(RNG&& rng, std::false_type) // それ以外
{
    std::uniform_real_distribution<T> uni(0, 100);
    return uni(rng);
}

template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
    return generate_random_impl<T>(std::forward<RNG>(rng), std::is_integral<T>{});
}
```

`std::is_integral`には色々な整数型への特殊化が用意されていて、整数値型の場合は`std::true_type`から、そうでない場合は`std::false_type`から派生しています。なのでその値を作って渡せば、オーバーロードを介して適切な方を選ぶことが出来ます。

これは動きますが、問題があります。整数型でないものは全て浮動小数点型だと思っている点です。この関数には整数型か浮動小数点型のどちらかしか渡してほしくないので、例えば大元の関数でそれを確認するなどの対策をとるべきでしょう。

```cpp
template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
    static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
                  "generate_random accept only Integer or Real values");
    return generate_random_impl<T>(std::forward<RNG>(rng), std::is_integral<T>{});
}
```

もちろん、以上の解決策は二者択一だからできることです。3つ、4つで分岐しようとするなら、複数回上記のディスパッチを行うか、`true_type`と`false_type`をやめて、タグに使える構造体をいくつか自作して、それを返すtraitクラスを用意する必要があるでしょう。

例として、標準ライブラリでは、`iterator_category`がその用途で使われています。`iterator_traits<T>::iterator_category`でタグ型を取り出すことができます。
例えば2つのイテレータ間の要素数を計算する`std::distance`関数では、`std::random_access_iterator`が来たときだけ引き算で値を返し、そうでなければ2つのイテレータが一致するまでインクリメントするという実装になっています。

* [distance - cpprefjp](https://cpprefjp.github.io/reference/iterator/distance.html)

## SFINAEとenable_if

今回に関しては、よりよいやり方があります。SFINAEと`std::enable_if`です。

SFINAEはオーバーロード解決の途中に実体化に失敗する`template`があっても、他にマッチするオーバーロードがあればコンパイルエラーにはならないというルールです。一つちゃんと解決できるオーバーロードがあるなら、他のオーバーロードを使おうとした時にどうなろうと、ちゃんと動くものを使って動いて欲しいですよね？　SFINAEは名前と使われ方が奇妙なので恐れられていますが（要出典）、単にこれだけのことです。

また、`std::enable_if`はコンパイル時条件が真の時は`enable_if::type`を持つものの、偽の時は持たないという型です。なので、中で`std::enable_if<条件>::type`を使っている関数は、条件が真でないと実体化に失敗する関数になります。なにせ条件が偽なら、そんな型は存在しないので！

これらを使うと、条件式が偽のときは`template`の実体化に失敗するのでオーバーロード解決の候補にならず、コンパイラにスルーされる関数を作ることが出来ます。

```cpp
template<typename T, typename RNG,
         std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
T generate_random(RNG&& rng)
{
    std::uniform_int_distribution<T> uni(0, 100);
    return uni(rng);
}

template<typename T, typename RNG,
         std::enable_if_t<std::is_floating_point_v<T>, std::nullptr_t> = nullptr>
T generate_random(RNG&& rng)
{
    std::uniform_real_distribution<T> uni(0, 100);
    return uni(rng);
}
```

上の関数は`std::is_integral_v<T>`が`true`の時だけ、3つ目の`template`引数が定義されます。そうでない場合定義されず、存在しない型を受け取ることになり、実体化に失敗します。

なので、もし`double`が`generate_random`に渡された場合、コンパイラは例えば先に上の関数に`double`を入れてみて失敗し（3つめの`template`引数が定義できないため）、次に下の関数に入れて成功してお祝いをするでしょう。

ちなみに`enable_if`は`template`引数以外にも使うことができます。例えば引数や、

```cpp
template<typename T, typename RNG>
T generate_random(RNG&& rng, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr)
{
    std::uniform_int_distribution<T> uni(0, 100);
    return uni(rng);
}
```

戻り値でもokです。

```cpp
template<typename T, typename RNG>
std::enable_if_t<std::is_integral_v<T>, T>
generate_random(RNG&& rng)
{
    std::uniform_int_distribution<T> uni(0, 100);
    return uni(rng);
}
```

`enable_if::type`は第2`template`引数に渡した型になるので、時と場合に応じて適切な型を与えましょう。
普通`enable_if`の存在は呼び出し側に意識させたくないので、デフォルト引数を渡すのが主流ですが、そのときに渡す値が一つしか存在しないという点で便利な`nullptr_t`が使われています。

* [std::enable_ifを使ってオーバーロードする時、enablerを使う？](https://qiita.com/kazatsuyu/items/203584ef4cb8b9e52462)

ちなみにC++11以前は関数にデフォルトテンプレート引数を渡せなかったため、引数か戻り値のどちらかしか使われて来ませんでした。

* [関数テンプレートに対するデフォルトテンプレート引数の使い方](http://d.hatena.ne.jp/gintenlabo/20110302/1299093091)

## C++17なら？

さて、上記まではC++11でも（書き方は少し制限されますがC++98でも）可能な手法ばかりでしたが、最新規格ではどうできるでしょう？　新機能はプログラマを幸せにするために追加されるものなので、こういうことをするのも簡単になっているはずですよね？

C++17では`constexpr if`がコア言語に入ったので、以下のようなことができます。

```cpp
template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
    if constexpr (std::is_integral_v<T>)
    {
        std::uniform_int_distribution<T> uni(0, 100);
        return uni(rng);
    }
    else if constexpr (std::is_floating_point_v<T>)
    {
        std::uniform_real_distribution<T> uni(0, 100);
        return uni(rng);
    }
    else
    {
        static_assert(false_v<T>);
    }
}
```

`if constexpr (条件)`で、条件に合わない`if`節の実体化を抑制することが可能です。
コンパイラは、関数`template`が何か具体的な型をあてがわれて呼ばれた時に、ジェネリックなコードに型引数を代入して関数の実際の実装を作り出します。`constexpr if`は、コンパイラが実装を作り出す時に、一部分を無視させる方法です。

もしここで普通の`if`を使っていたらどうなるでしょう。コンパイラは`template`引数に渡された型のそれぞれについて関数全体を実体化しますから、`int`が来た場合でも`std::uniform_real_distribution<int>`を実体化しようとします。そしてエラーになるでしょう。

しかし`constexpr if`は条件にそぐわない部分の実体化をしないので、難を逃れることができます。

（※最後の`false_v`は、`static_assert`が「`else`節において`template`の実体化をした」場合のみコンパイルエラーにするために必要なものです。詳しくは『江添亮の詳説C++17』を読んでください。）

## あとがき

古典的な手法ですが、タグディスパッチやSFINAEに関してあまり複雑にならない例を見つけた気がしたので書いてみました。
これを機に興味を持った人が現れてくれれば幸いです。

## おまけ

値の範囲が関係ないなら、そんな変な技法を習得しなくても、今回使ってる型の値は要するにビット列なんだからビットをランダムに埋めればいいじゃん！　という発想のコードが以下です。

```cpp
template<typename T, typename RNG>
T generate_random(RNG&& rng)
{
    T value;
    std::uint8_t* iter = reinterpret_cast<std::uint8_t*>(std::addressof(value));
    std::uniform_int_distribution<std::uint8_t> uni(0x00u, 0xFFu);
    for(std::size_t i=0; i<sizeof(T); ++i)
    {
        *(iter++) = uni(rng);
    }
    return value;
}
```

残念ながらこれは浮動小数点数については意図していた通りに動きません。最初に想定していたのは普通の数（今回は適当に1から100にしましたが）がランダムに返ってくるというものですが、浮動小数点数には普通の数値でない特別な値があります。`Inf`と`NaN`です。NaNの表現は「指数部ビットが全て1で仮数部ビットが非ゼロ」なので、それなりの確率で出現します。`Inf`は仮数部がゼロでなければならないので、だいぶ確率が低いです。