C++テンプレートの学習にstatic_assertだけで『テスト駆動開発』多国通貨を写経する

# はじめに

C++ Template Meta Programmingの学習がしたくてやりました。

C++の勉強を始めて半年近く、そろそろテンプレートに手を付けたいと考えていました。けど、Template Meta Programmingって敷居高いですよね。既存ライブラリになんて中々手が出せないですし。
悶々とどうしようかな、と考えていたところ、別件で[Kent Beckのテスト駆動開発](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA-Kent-Beck/dp/4274217884/ref=sr_1_1?ie=UTF8&qid=1525496259&sr=8-1&keywords=%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA)をパラパラと見返している時、ふと思いつきました。

**これ、static_assertだけでやったらテンプレートの練習にならんかな？**

結果として、一部不満が残るものの、テンプレートを使ってstatic_assertだけで一通り写経を完了することができました。
**十分に小さいステップで1つずつテンプレートテクニックを試すことができ、楽しく学習できると感じました**。テンプレートやってみたいけど、なかなか一歩が踏み出せない方々の参考になるかと思い、やったこと、学習できたこと、ハマったところ、を共有します。

## なぜ、多国通貨の写経をstatic_assertだけで？

Template Meta Programmingを勉強したいと考えており、**動いて楽しく、かつ、ぼちぼちの難易度の課題**を探していました（楽しいは正義）。
主要な用途がライブラリ実装なためか、自分で何かを軽く作ってみたいなぁ、に対する良い感じの課題が思いつきませんでした。平凡なプログラマなので、既存ライブラリに手を付けるのは壁が高かったです。
また、（非常に参考にさせて頂いていますが）既存の書籍や記事はテクニック集が多く、軽く遊べるものが欲しい、という目的に沿うものが見つけられませんでした。

おそらく、テンプレートがライブラリ実装で効力を発揮するので、今回やったアプリケーションでの利用は本来の趣旨から外れていると思います。実際に、テンプレートで重要なテクニックであるはずの、decltypeやvariadic templateなどは試せていません。あくまで、テンプレート初心者が最初に遊んでみて足掛かりにする、という趣旨です。

### 『テスト駆動開発』多国通貨の例

単純すぎず、難しすぎない程度の難易度です（書籍を読んだことのある方はわかると思います）。ポリモーフィズムの扱いもあり、**各ステップを非常に小さく**記述してくれています。

### static_assert

[コンパイル時アサート](https://cpprefjp.github.io/lang/cpp11/static_assert.html)

> static_assert宣言は、指定した定数式が真であることを表明するための機能である。
> これは、コンパイル時に満たされるべき要件を検証するために使用できる。

つまり、静的に全ての値が確定している必要があります。これは**ポリモーフィズムも含めて全て静的に解決する**必要があることを意味します。

### 多国通貨の例×static_assert

多国通貨で動的ポリモーフィズムを使っている部分を、全てテンプレートで静的ポリモーフィズムにより、解決します。
書籍通り進めれば、**各ステップは十分に小さいため、テンプレートテクニックを1つずつ試しながら、学習できる**と考えました。

# やったこと

[テスト駆動開発](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA-Kent-Beck/dp/4274217884/ref=sr_1_1?ie=UTF8&qid=1525496259&sr=8-1&keywords=%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA)第1部～多国通貨～(第1章-第16章)のテストケースを全てstatic_assertで書いて、テストをパスするコードを書きました。

## 開発環境

参考までに。

| 項目 | 内容 |
| --- | --- |
| OS | Ubuntu 16.04 |
| ビルドツール | cmake 3.11.2 (**minimum 3.9**) |
| コンパイラ | ~~gcc 5.4.0 (c++14)~~ **gcc 8.0.1 (c++17)** |
| テストフレームワーク | googletest (何でもよかった) |

~~とりあえずお仕事（で使っているクロスコンパイラ）でも使えるgcc versionとc++14を対象としています。~~
constexprなmap実現のために、c++17を使用することにしました(2018/5/20)。
コンパイルエラー発生時、何度か[Wandbox](https://wandbox.org/)(使ってコンパイラ依存のエラーでないか、を確認しました。

# 学習できたこと

※完全理解ではないです。

## constexpr色々（コンストラクタ、関数など）

お題上、ありとあらゆるところでconstexprを書くので、constexprの勉強になります。
ハマったこと、に詳細を記載しますが、c++11⇒c++14でconstexprの仕様にかなりの改善があり、サンプルコードがコンパイルエラーになったり、苦しみながらも勉強できました。

## SFINAE (Substitution Failure Is Not An Error)

第7章～疑念をテストに翻訳する～で、*Dollar*オブジェクトと*Franc*オブジェクトを比較すると、等価でない、と判定する処理を実装します。ここで、SFINAEを使いました。
他にもっと良いやり方がある気がしますが、今はわかりません。今後の学習課題です。

```cpp
// T, UがMoneyを基底クラスとしており、同じクラスのオブジェクトである場合、amountを参照して等価性を評価する。
// ==オペレータにDollarとFrancを渡すとこのテンプレートへの置き換えに失敗するが、即時エラーにならず、下のテンプレートを展開しにいってくれる。
template <class T, class U,
            typename std::enable_if<
              std::is_base_of<Money, T>::value &&
              std::is_base_of<Money, U>::value &&
              std::is_same<T, U>::value
            >::type* = nullptr
          >
constexpr bool operator==(const T& rhs, const U& lhs) {
  return rhs.amount() == lhs.amount();
}

// T, UがMoneyを基底クラスとしているが、別クラスのオブジェクトである場合、一律で等価でない、と評価する。
template <class T, class U,
            typename std::enable_if<
              std::is_base_of<Money, T>::value &&
              std::is_base_of<Money, U>::value &&
              !std::is_same<T, U>::value
            >::type* = nullptr
          >
constexpr bool operator==(const T&, const U&) {
  return false;
}
```

↑は作成途中のもので、この後、グリーン状態でもう少し試行錯誤していました。
**グリーン状態でテンプレートテクニックを試行錯誤できる**、というは初期の学習において、かなりのメリットと感じました。

## CRTP (Curiously Reccursive/Reccuring Template Pattern)

第9章～歩幅の調整～では、抽象メソッドcurrency()が定義された*Money*を基底クラスとして、派生クラスの*Dollar*と*Franc*を実装します。
ポリモーフィズムを利用するため、テンプレートで静的ポリモーフィズムを実現できるCRTPを使いました。
教科書通りで実装できました。

```cpp:ソースコード
// 通貨識別用
enum class Currency {
  kUSD, kCHF, kNoCurrency
};

// テンプレートパラメータとして派生クラスの型を持つ。
template <class Derived>
class Money {
 public:
    constexpr Money(int32_t amount) :amount_{amount} {}
    // currency()は保持している通貨を返す
    constexpr Currency currency() const {
      // 派生クラスのcurrency()を呼び出す。
      return static_cast<const Derived&>(*this).currency();
    }
...
};

class Dollar : public Money<Dollar> {
 public:
    constexpr Dollar(int32_t amount) : Money{amount} {}
    constexpr Currency currency() const {
      // DollarクラスはUSDを返す
      return Currency::kUSD;
    }
};
```

```cpp:テストコード
TEST_F(MoneyTest, Currency) {
  // Dollarをテンプレートパラメータに指定して、Moneyのインスタンスを作る。currency()はDollarクラスのcurrency()を呼び出す。
  static_assert(Money<Dollar>{1}.currency() == Currency::kUSD, "Dollar must have USD currency.");
  static_assert(Money<Franc>{1}.currency() == Currency::kCHF, "Franc must have CHF currency.");
}
```

書籍の後半では、*Expression*を基底クラスとして、派生クラスの*Money*と*Sum*を実装します。
こちらは、*Dollar*と*Franc*の実装よりもかなり苦労しました。何回か積んだか？と思うこともありました。

### 書籍を読んでない方向けの補足

Expressionは抽象メソッドreduce()を定義する基底クラスです。reduce()は式を単純な形に変形（簡約）するという意味があります(『テスト駆動開発pp.83 翻訳者注』)。
つまり、何らかの式(*Expression*オブジェクト)に対して、reduce()すると、お金(*Money*)が得られるようにします。
例えば、*5ドル+10フラン*という式を与えると、銀行(*Bank*)が欲しい通貨(*Currency*)の為替レートに基づいて、お金(*Money*)を返してくれる、という寸法です。

```cpp:使い方のサンプル
  // 10フラン＝5ドルの為替レート
  auto sum = money::dollar(5) + money::franc(10);
  Money result = sum.reduce(bank, Currency::kUSD);
  static_assert(result == money:dollar(10), "5 USD + 10 Franc = 10 USD.");
```

実際のシーケンスは、次のような呼び出しになります。

```
Bank::reduce(bank, to)
  ⇒Sum<Expression<Sum>>::reduce(bank, to)  // 5 USD + 5 USD
    ⇒Money<Expression<Money>>::reduce(bank, to) // 5 USD ⇒ 5 USD
    ⇒Money<Expression<Money>>::reduce(bank, to) // 10 Franc ⇒ 5 USD
```

### Expression, Money, Sumの実装

Expressionは派生クラスの型をテンプレートパラメータに持ち、抽象メソッドreduce()を定義しています。教科書通りのCRTPで、特にひねりはありません。
Bankがintのテンプレートパラメータを持っていてダサい理由は、ハマったことで後述します。

```cpp:Expressionの実装
// Forward declarations.
class Money;
template <int32_t N>
class Bank;

template <class Derived>
class Expression {
 public:
    constexpr Expression() = default;
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const;
};

// Moneyの定義

template <class Derived>
template <int32_t N>
constexpr Money Expression<Derived>::reduce(const Bank<N>& bank, const Currency to)const {
  return static_cast<const Derived&>(*this).reduce(bank, to);
}
```

Moneyも教科書通りです。Moneyは自身の金額を返す式(*Expression*)です。
Bankから、為替レート(自身の通貨⇒変換対象の通貨)を受け取り、新しいMoneyオブジェクトを作成して返しています。

```cpp:Moneyの実装
class Money : public Expression<Money> {
 public:
    constexpr Money(int32_t amount, Currency currency = Currency::kNoCurrency)
        : amount_{amount}, currency_{currency} {}

    // Implements Expression interface.
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const;

// 中略

 private:
    int32_t amount_;
    Currency currency_;
};

// Bankの定義

template <int32_t N>
constexpr Money Money::reduce(const Bank<N>& bank, const Currency to) const {
  int rate = bank.rate(currency_, to);
  return Money(amount_ / rate, to);
}
```

苦労したSumの実装です。Sumはaugend(足される数)とaddend(足す数)を持ち、その合計を返す式(*Expression*)です。
augendとaddendの型をテンプレート化する、という方法に至れば、すぐでした(が、散々頭を悩ませました。なぜかオーバーロードで何とかしようとしたり…)。
SumはMoney+Moneyを表す場合もあれば、**Sum+Money**を表す場合もあります(Sum+Sumの場合もあるでしょう)。augend/addendがSumの場合、Sumは、augend<Sum>/addend<Sum>のreduce()を呼び出します。ほぼ、Compositeパターンですね。

```cpp:Sumの実装
template <class T, class U>
class Sum : public Expression<Sum<T, U>> {
 public:
    constexpr Sum(const T& augend, const U& addend)
        : augend_{augend}, addend_{addend} {}

    // Implements Expression interface.
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const {
      auto amount = augend_.reduce(bank, to).amount()
         + addend_.reduce(bank, to).amount();
      return Money{amount, to};
    }

 private:
    T augend_;
    U addend_;
};
```

# ハマったこと

## c++11⇒c++14 非静的メンバ関数の、暗黙のconst修飾を削除

Moneyクラスのconstexprコンストラクタを作るところからスタートしました。
で、いきなりハマりました。

[cpprefjp constexpr](https://cpprefjp.github.io/lang/cpp11/constexpr.html)のサンプルがコンパイルエラーになったのです。

### 2018/5/13追記

@yumetodo さんに[fix(C++11constexpr): constexpr member function should be const member…](https://github.com/cpprefjp/site/commit/6e23ce54b67224f6f450cfd4ab4455194e56e42d)で反映頂きました。

```cpp:cpprefjpのconstexprコンストラクタサンプル
class Integer {
  int value_;
public:
  constexpr Integer(int value)
    : value_(value) {}

  constexpr int get()
  { return value_; }
};

int main()
{
  constexpr Integer x = 3;
  static_assert(x.get() == 3, "x value must be 3");
}
```

[Wandboxコンパイルエラー再現](https://wandbox.org/permlink/VwaiwPbJx6SYxZj7)

> prog.cc:14:23: error: passing 'const Integer' as 'this' argument discards qualifiers [-fpermissive]
>   static_assert(x.get() == 3, "x value must be 3");

↑のようにconst修飾に関するエラーが発生します。

実験していると[c++11ではコンパイルエラーにならない](https://wandbox.org/permlink/tUxEEkwAb1jqQZ9Y)ことがわかりました。
c++11⇒c++14で、[非静的メンバ関数の、暗黙のconst修飾を削除](https://cpprefjp.github.io/lang/cpp14/relaxing_constraints_on_constexpr.html)という仕様改善があったことが分かり、メンバ関数にconst修飾することで解決しました。

## まだハマり中のもの

### constexprなmap

Bankは為替レートのテーブルを持っています。このテーブルは為替レートの追加が可能なように作られています（書籍内では）。
追加可能なmapをconstexprでどのように表現すれば良いのか、が分かっておらず、未だに誤魔化し実装になっています。

```cpp:無様なBankの実装
// 持っている為替レート数をテンプレートパラメータにしている
template <int32_t N>
class Bank {
  using Hash = std::pair<const Currency, const Currency>;
  using Rate = std::pair<Hash, int>;
 // std::arrayで為替レートテーブルを表現
 private:
    std::array<Rate, N> rates_;

 public:
    constexpr Bank(const std::array<Rate, N>& rates) : rates_{rates} {}

    // 為替レート数を+1したBankを新しく作る
    constexpr Bank<N+1> addRate(const Currency from, const Currency to, int32_t rate) const {
      // 既存のarrayに1つ為替レートを追加する方法が分からず、為替レートは1つに固定されている…。
      std::array<Rate, 1> rates = { Rate{Hash{from, to}, rate} };  // ☆
      return Bank<N+1>{{rates}};
    }
};
```

☆のところで、↓のような感じのことがしたいです。std::arrayをinitializer listに変換できれば良いと思うのですが…。

```cpp
  std::array<Rate, N+1> new_rates = { rates_, Rate{Hash{from, to}, rate} };
```

c++20でvectorをconstexprにしようとしているみたいですが、そこまでは待てないですね…。

#### c++17 std::arrayによる解決 2018/5/20追記

c++17では、std::array要素の参照がconstexprになりました。(c++14ではconst参照はconstexpr)
この修正により、constexpr内でstd::arrayの要素が読み書きできます。

```cpp:arrayをconstexpr関数で更新する
template <int N>
constexpr static std::array<int, N> createTable() {
  std::array<int, N> a{};
  for (int i = 0; i < N; ++i) {
    a[i] = i;
  }
  return a;
}

int main() {
  constexpr auto t1 = createTable<1>();
  constexpr auto t2 = createTable<1>();
  static_assert(t1[0] == t2[0], "can build.");
}
```

[c++14の結果(コンパイルエラー)](https://wandbox.org/permlink/13yyUxxlHBLhtOyn)
[c++17の結果](https://wandbox.org/permlink/Kd2jBTW9xySybski)

これと、コメント欄で頂いたアドバイスを組み合わせて、なんとか形になりました。@yumetodoさん、@ygsiroさん、ありがとうございました。

主な変更点

- c++17のstd::arrayを使う
- Rateをstd::pairでなくユーザー定義型にする(std::pairはconstexprな代入不可？)
- mapをimmutableな固定長配列にする(取り扱い通貨数からmapのエントリ数を固定で算出)

```cpp:やや無様でなくなったBankの実装
// もにょもにょとヘルパー関数を準備
namespace internal {
// A currency is exchanged to the other (means, n-1) currencies.
constexpr static size_t CalcTradingRateEntry(const uint32_t n) {
  return n*(n-1);
}

constexpr static size_t kNumRateEntry
    = CalcTradingRateEntry(kNumTradingCurrency);

constexpr size_t FindExistingEntry(const std::array<Rate, kNumRateEntry>& rates,
                                   const Rate& target, const size_t filled_index) {
  size_t i = 0;
  for (; i < filled_index; ++i) {
    if (rates[i] == target)
      break;
  }
  return i;
}
}  // namespace internal

// Bankはテンプレート不要になった
class Bank {
 public:
    constexpr Bank(): rates_{}, index_for_new_entry_{0} {}
    constexpr Bank(const std::array<Rate, internal::kNumRateEntry>& rates, const size_t filled)
        : rates_{rates}, index_for_new_entry_{filled} {}
 
    // Implements Expression interface.
    template <class T>
    constexpr Money reduce(const Expression<T>& source, const Currency to) const {
      return source.reduce(*this, to);
    }

    constexpr Bank addRate(const Currency from, const Currency to, const int32_t rate) const {
      // 既存の変換レートmapをコピーコンストラクトする
      std::array<Rate, internal::kNumRateEntry> new_rates{rates_};
      size_t index_for_new_entry = index_for_new_entry_;
      const Rate target(from, to, rate);

      // 新しいエントリ挿入 or 既存エントリの更新
      size_t index = internal::FindExistingEntry(rates_, target, index_for_new_entry_);
      if (index == index_for_new_entry_) {  // 新しいエントリの挿入
        new_rates[index] = target;
        index_for_new_entry++;
      } else {  // 既存エントリの更新
        new_rates[index].rate = rate;
      }

      return Bank{new_rates, index_for_new_entry};
    }
    constexpr int32_t rate(const Currency from, const Currency to) const {
      if (from == to) return 1;
      return findRate(from, to);
    }

 private:
    constexpr int32_t findRate(const Currency from, const Currency to) const {
      Rate target(from, to, 1);
      for (size_t i = 0; i < index_for_new_entry_; ++i) {
        if (rates_[i] == target)
          return rates_[i].rate;
      }
      return 0;
    }

 private:
    // mapは固定長配列で確保する
    std::array<Rate, internal::kNumRateEntry> rates_;
    // 新しい通貨変換レートが追加されたときに使う配列の添字
    size_t index_for_new_entry_;
};
```

もう少しうまく作れる気がしますが、一旦満足しました。
今後は、c++14で動くようにしていきたいですね。

### Sum宣言の型

*5 USD + 10 Franc + 5 USD*を表す式(*Expression*)を作ろうとすると、今は↓のように書かないとダメです。イケてません。
ヘルパー関数を用意して、型推論させれば、できそう、ということしか分かっていません。

```cpp
TEST_F(MoneyTest, SumPlusMoney) {
  constexpr auto five_bucks = money::dollar(5);
  constexpr auto ten_francs = money::franc(10);
  constexpr auto sum = Sum<Sum<Money, Money>, Money>{
    Sum<Money, Money>{five_bucks, ten_francs} + five_bucks};
}
```

### 2018/5/13追記

@yumetodo さんより指摘を頂き、下記でコンパイル可能であることを確認しました。

```cpp
  constexpr auto five_bucks = money::dollar(5);
  constexpr auto ten_francs = money::franc(10);
  constexpr auto sum = (five_bucks + ten_francs) + five_bucks;
}
```

# ソースコード

[github](https://github.com/tomoyuki-nakabayashi/TestDrivenDevByExample/tree/master/CppTemplate)

```cpp:ソースコード
namespace money {

// Forward declarations.
class Money;
template <class T, class U>
class Sum;
template <int32_t N>
class Bank;

enum class Currency {
  kUSD, kCHF, kNoCurrency
};

template <class Derived>
class Expression {
 public:
    constexpr Expression() = default;
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const;
};

class Money : public Expression<Money> {
 public:
    constexpr Money(int32_t amount, Currency currency = Currency::kNoCurrency)
        : amount_{amount}, currency_{currency} {}

    // Implements Expression interface.
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const;

    constexpr friend bool operator==(const Money& lhs, const Money& rhs) {
      return (lhs.amount_ == rhs.amount_) && (lhs.currency_ == rhs.currency_);
    }

    // Accessors.
    constexpr Currency currency() const {
      return currency_;
    }
    constexpr int32_t amount() const{
      return amount_;
    }

 private:
    int32_t amount_;
    Currency currency_;
};

template <int32_t N>
class Bank {
  using Hash = std::pair<const Currency, const Currency>;
  using Rate = std::pair<Hash, int>;

 public:
    constexpr Bank(const std::array<Rate, N>& rates) : rates_{rates} {}
 
    // Implements Expression interface.
    template <class T>
    constexpr Money reduce(const Expression<T>& source, const Currency to) const {
      return source.reduce(*this, to);
    }

    constexpr Bank<N+1> addRate(const Currency from, const Currency to, int32_t rate) const {
      std::array<Rate, 1> rates = { Rate{Hash{from, to}, rate} };
      return Bank<N+1>{{rates}};
    }
    constexpr int32_t rate(const Currency from, const Currency to) const {
      if (from == to) return 1;
      return findRate(from, to);
    }

 private:
    constexpr int32_t findRate(const Currency from, const Currency to) const {
      for (auto i = 0; i < N; ++i) {
        auto hash = rates_[i].first;
        if (hash.first == from && hash.second == to)
          return rates_[i].second;
      }
      return 0;
    }

 private:
    std::array<Rate, N> rates_;
};

template <class T, class U>
class Sum : public Expression<Sum<T, U>> {
 public:
    constexpr Sum(const T& augend, const U& addend)
        : augend_{augend}, addend_{addend} {}

    // Implements Expression interface.
    template <int32_t N>
    constexpr Money reduce(const Bank<N>& bank, const Currency to) const {
      auto amount = augend_.reduce(bank, to).amount()
         + addend_.reduce(bank, to).amount();
      return Money{amount, to};
    }

    // Accessors.
    constexpr T augend() const { return augend_; }
    constexpr U addend() const { return addend_; }

 private:
    T augend_;
    U addend_;
};

template <class Derived>
template <int32_t N>
constexpr Money Expression<Derived>::reduce(const Bank<N>& bank, const Currency to)const {
  return static_cast<const Derived&>(*this).reduce(bank, to);
}

template <int32_t N>
constexpr Money Money::reduce(const Bank<N>& bank, const Currency to) const {
  int rate = bank.rate(currency_, to);
  return Money(amount_ / rate, to);
}

// Factory methods.
constexpr Money dollar(int32_t amount) {
  return Money{amount, Currency::kUSD};
}

constexpr Money franc(int32_t amount) {
  return Money{amount, Currency::kCHF};
}

// Operators.
template <class T, class U>
constexpr Sum<T, U> operator+(const Expression<T>& lhs, const Expression<U>& rhs) {
  return Sum<T, U>{static_cast<T const&>(lhs), static_cast<U const&>(rhs)};
}

template <class T>
constexpr T operator*(const Expression<T>& lhs, const int32_t multiplier) {
  return T(static_cast<T const&>(lhs).augend() * multiplier,
           static_cast<T const&>(lhs).addend() * multiplier);
}

constexpr Money operator*(const Money& lhs, const int32_t multiplier) {
  return Money{lhs.amount() * multiplier, lhs.currency()};
}

}  // namespace money
```

```cpp:テストコード
namespace money_test{
using money::Money;
using money::Currency;
using money::Expression;
using money::Bank;
using money::Sum;

class MoneyTest : public ::testing::Test {
};

TEST_F(MoneyTest, Multiplication) {
  constexpr auto five = money::dollar(5);
  static_assert((five*2) == money::dollar(10), "product must be 10.");
  static_assert((five*3) == money::dollar(15), "product must be 15.");
}

TEST_F(MoneyTest, Equality) {
  static_assert(money::dollar(5) == money::dollar(5), "Two objects must be same.");
  static_assert(!(money::dollar(5) == money::dollar(6)),
    "Two objects must be different.");
  static_assert(!(money::franc(5) == money::dollar(5)),
    "Two objects must be different.");
}

TEST_F(MoneyTest, Currency) {
  static_assert(money::dollar(1).currency() == Currency::kUSD,
    "Dollar must have USD currency.");
  static_assert(money::franc(1).currency() == Currency::kCHF,
    "Franc must have CHF currency.");
}

TEST_F(MoneyTest, SimpleAddition) {
  constexpr Money five = money::dollar(5);
  constexpr auto sum = five + five;
  constexpr Bank<0> bank{{}};
  constexpr Money reduced = bank.reduce(sum, Currency::kUSD);
  static_assert(reduced == money::dollar(10), "sum must be 10 USD.");
}

TEST_F(MoneyTest, PlusReturnsSum) {
  constexpr Money five = money::dollar(5);
  constexpr auto sum = five + five;
  static_assert(sum.augend() == five, "Augend must be five dollar.");
  static_assert(sum.addend() == five, "Addend must be five dollar.");
}

TEST_F(MoneyTest, ReduceSum) {
  constexpr auto sum = money::dollar(3) + money::dollar(4);
  constexpr Bank<0> bank{{}};
  constexpr Money result = bank.reduce(sum, Currency::kUSD);
  static_assert(result == money::dollar(7), "Result must be seven dollar.");
}

TEST_F(MoneyTest, ReduceMoney) {
  constexpr Bank<0> bank{{}};
  constexpr Money result = bank.reduce(money::dollar(1), Currency::kUSD);
  static_assert(result == money::dollar(1), "Result must be one dollar.");
}

TEST_F(MoneyTest, ReduceMoneyDifferentCurrency) {
  constexpr Bank<0> bank{{}};
  constexpr auto new_bank = bank.addRate(Currency::kCHF, Currency::kUSD, 2);
  constexpr Money result = new_bank.reduce(money::franc(2), Currency::kUSD);
  static_assert(result == money::dollar(1), "Two franc must be one dollar.");
}

TEST_F(MoneyTest, IdentityRate) {
  constexpr Bank<0> bank{{}};
  static_assert(bank.rate(Currency::kUSD, Currency::kUSD) == 1,
    "Must be one if two currecy are same.");
}

TEST_F(MoneyTest, MixedAddition) {
  constexpr auto five_bucks = money::dollar(5);
  constexpr auto ten_francs = money::franc(10);
  constexpr Bank<0> empty_bank{{}};
  constexpr auto bank = empty_bank.addRate(Currency::kCHF, Currency::kUSD, 2);
  constexpr Money result = bank.reduce(five_bucks + ten_francs, Currency::kUSD);
  static_assert(result == money::dollar(10), "5 USD + 10 CHF must be 10 USD.");
}

TEST_F(MoneyTest, SumPlusMoney) {
  constexpr auto five_bucks = money::dollar(5);
  constexpr auto ten_francs = money::franc(10);
  constexpr Bank<0> empty_bank{{}};
  constexpr auto bank = empty_bank.addRate(Currency::kCHF, Currency::kUSD, 2);
  constexpr auto sum = Sum<Sum<Money, Money>, Money>{
    Sum<Money, Money>{five_bucks, ten_francs} + five_bucks};
  constexpr Money result = bank.reduce(sum, Currency::kUSD);
  static_assert(result == money::dollar(15), "5 USD + 10 CHF + 5 USD must be 15 USD.");
}

TEST_F(MoneyTest, SumTimes) {
  constexpr auto five_bucks = money::dollar(5);
  constexpr auto ten_francs = money::franc(10);
  constexpr Bank<0> empty_bank{{}};
  constexpr auto bank = empty_bank.addRate(Currency::kCHF, Currency::kUSD, 2);
  constexpr auto sum = Sum<Money, Money>{five_bucks, ten_francs} * 2;
  constexpr Money result = bank.reduce(sum, Currency::kUSD);
  static_assert(result == money::dollar(20), "(5 USD + 10 CHF) *2 must be 20 USD.");
}

}  // namespace money_test
```

# 最後に

正確に計測していたわけではないですが、着手してから25時間程度で2018/5/6時点のソースとテストを作れました。
これを学習効果が高いと言えるかどうかは、判断が難しいですが、終始楽しかったです。
テストを頼りに細かくテンプレートテクニックを試せるのも良かったと思います。

C++テンプレートに手を出せないでいる方は、多国通貨を通じて入門してみてはいかがでしょうか。

# 参考

参考にした書籍、サイト一覧です。非常に参考になりました。ありがとうございました。

[テスト駆動開発](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA-Kent-Beck/dp/4274217884/ref=sr_1_1?ie=UTF8&qid=1525496259&sr=8-1&keywords=%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA)
[C++テンプレートテクニック 第2版](https://www.amazon.co.jp/C-%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-%E7%AC%AC2%E7%89%88-%CE%B5%CF%80%CE%B9%CF%83%CF%84%CE%B7%CE%BC%CE%B7/dp/4797376686)
[Wandbox](https://wandbox.org/)
[cpprefjp - C++日本語リファレンス](https://cpprefjp.github.io/)
[本の虫](https://cpplover.blogspot.jp/)
[C++メタ関数のまとめ](https://qiita.com/_EnumHack/items/ee2141ad47915c55d9cb)