非型テンプレートパラメータに浮動小数点数を渡す方法(C++2aバージョン)

最近、以前書いた
[非型テンプレートパラメータに浮動小数点数を渡す方法](https://qiita.com/kazatsuyu/items/1656e90b8ac0d999f12d)
に通知が２個も来まして、

[「非型テンプレートパラメータに浮動小数点数を渡す方法」をC++11に移植してみた](https://qiita.com/2bbb/items/6d90c85950646d48d69e)
[【C++】任意の非型テンプレートパラメータをもたせる](https://qiita.com/168irohairoha/items/9560d16b94fc4233e37d)

という記事が発表されていたので、元ネタ著者としては改めて最新の規格で実装することを考えてみようかな、という次第です。

さて、C++標準化委員会では規格に新しい仕様を追加するときに、Proposalと呼ばれる文書を用意し、議論を経て修正を加えていく手法を取っています。
一覧は[ここ](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/)から見ることができまして、最新の規格を追いかけるのが大好きな人々はこれを読み漁っています（ホンマか？）
その中の1つに、 [Class Types in Non-Type Template Parameters](http://wg21.link/p0732) という提案がありまして、これは次期C++2aに盛り込まれることが内定しております。

これはタイトル通り、非型テンプレートパラメータにクラス型を使えるようにする提案です。
非型テンプレートパラメータの仕様はこれによって大きく変わり、 左辺値参照以外では *strong structural equality* という条件を満たすリテラル型の値をとることができるようになりました。

GCCでは既に試すことができるようなのでコンパイラの挙動と規格を突き合わせながら四苦八苦して解読を試みていたところ、

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://t.co/JASwUpgKPz">https://t.co/JASwUpgKPz</a><br>解説記事あります！</p>&mdash; 🦈もかむくるが (@mokamukuruga) <a href="https://twitter.com/mokamukuruga/status/1110747466276626433?ref_src=twsrc%5Etfw">2019年3月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

と教えてもらいまして、 *strong structural equality* の条件はこちらの解説を読んでもらえばいいと思います。

基本的にはクラス型のビット表現が一致するときに等価(equal)であることが担保できるのであれば良いようですが、浮動小数点数は0.0と-0.0が異なるビット表現でも等し(equivalent)かったり、NaNとNaNが同じビット表現でも等しくなかったりするので *strong structural equality* を満たすことはできません。

ただし、以前の記事でもやったように、「浮動小数点数 ←→ *strong structural equality* を満たす型」の変換を実装してやれば、その型を引数にすることはできます。

さて、C++2aでは他にも色々機能が追加されているのですが、その中の一つに、 [`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast) というものがあります。
これは、同じサイズのトリビアルにコピー可能な型同士をビット表現そのまま変換できる関数で、 `double` から `std::uint64_t` への変換（ `double` が64bitの場合）などが簡単にできるようになります。つまり、以前の記事で必死にIEEE754の内部表現を分解してやっていたことが、この関数一発でできるようになるわけです。
しかも、以前の記事では浮動小数点数型と同一サイズの整数型を泥臭く調べていましたが、 `std::array<std::byte, sizeof(double)>` は `std::bit_cast` することもできるし、 *strong structural equality* も満たしています。
したがって、もはや浮動小数点数型のサイズも、内部表現の仕様も気にする必要はありません。
我々はこんな型を作ればいいのです。

```cpp
#include <array>
#include <bit>
#include <cstddef>

namespace alt_float {

template<typename T>
class float_t {
    using inner_type = std::array<std::byte, sizeof(T)>;
    alignof(T) inner_type value{};
public:
    constexpr float_t() noexcept = default;
    constexpr float_t(const float_t&) noexcept = default;
    constexpr float_t(float_t&&) noexcept = default;
    constexpr float_t(T v) noexcept: value{ std::bit_cast<inner_type>(v) } {}
    constexpr float_t& operator=(const float_t&) noexcept = default;
    constexpr float_t& operator=(float_t&&) noexcept = default;
    constexpr float_t& operator=(T v) noexcept {
        value = std::bit_cast<inner_type>(v);
        return *this;
    }
    constexpr operator T() const noexcept { return std::bit_cast<T>(value); }
};

}
```

算術演算なども定義してもいいですが、そこらへんは以前の記事で書いたし割愛します。

使い方は実に簡単。浮動小数点数を受け取りたいテンプレートパラメータのところで、

```cpp
template<alt_float::float_t<double> x>
class op;
```

などとするだけ。表記が冗長ならusingでエイリアスを定義してもいいでしょう。
動作確認に使ったGCCの実装がおかしくなければ、

```cpp
template<alt_float::float_t x>
class op;
```

とだけ書くことも可能な模様。この場合は`float`も`double`も`long double`も受け取れるようになります[^1]。

[^1]: ちなみに実はこのままだと`int`や`bool`なんかも受け取れてしまうので、C++2aらしくconstraintを書くとより良いと思いますがこの場では割愛します。

テンプレートパラメータに実引数を渡すときも明快で、

```cpp
op<3.14>
```

などと書けば良し。浮動小数点数型から暗黙変換できるので、ユーザー定義リテラルのような小細工も必要ありません[^2]。

[^2]: 逆に、暗黙変換をなくしてユーザー定義リテラルを用意しても良いでしょう。

余談。
さて実は、`std::bit_cast`が実装されたコンパイラがまだないので、上記のコードはコンパイルできません。
しかし、GCCはなぜか、メンバ変数に浮動小数点数を含んでいても*strong structural equality*と判断するようです[^3]。なので、bit_castを使用せずともそのままメンバ変数に浮動小数点数型を持つことができます。

[^3]: おそらくバグなのだと思います。

というわけで、上記のコードとは少し異なりますが、最後にデモを貼っておきます。
https://wandbox.org/permlink/IlG1AOjqtrfzakRO