C++ のタプルの全要素を逐次処理する。標準ライブラリのみ、 C++17 以上。

Boost を使わずに、std::tuple に使える拡張 for 文のようなものが欲しい人向け。C++17 以上。

```C++
#include <iostream>
#include <tuple>

/**
 * @fn void ApplyTuple(const F& func, const T& tpl, std::index_sequence<indices...>)
 * @brief タプル tpl の全要素に関数 func を適用する。
 * @param[in] func タプルの要素を引数にとる関数。
 * @param[in] tpl タプル。
 * @return void
 */
template <typename F, typename T, std::size_t... indices>
void ApplyTuple(const F& func, const T& tpl, std::index_sequence<indices...>) {
  using swallow = int[];
  (void)swallow {
    (func(std::get<indices>(tpl)), 0)... 
  };
};

/**
 * @fn R ApplyTuple(const F& func, const T& tpl, std::index_sequence<indices...>)
 * @brief タプル tpl の全要素に関数 func を適用し、その結果を得る。
 * @param[in] func タプルの要素を引数にとる関数。
 * @param[in] tpl タプル。
 * @return R タプル tpl の各要素に関数 func を適用させた値を要素に持つタプル。
 */
template <typename R, typename F, typename T, std::size_t... indices>
R ApplyTuple(const F& func, const T& tpl, std::index_sequence<indices...>) {
  R ret;
  using swallow = int[];
  (void)swallow {
    (ret = std::make_tuple(func(std::get<indices>(tpl))...), 0)
  };
  return ret;
};

int main(void) {
  /** 戻り値を受け取らない場合 */
  // タプルを生成。
  auto tpl1 = std::make_tuple("abc", 1, 1.2);
  // タプルのサイズを取得。
  constexpr std::size_t size1 = std::tuple_size<decltype(tpl1)>::value;
  // タプルの要素に適用する関数。ここでは、要素を標準出力。
  auto f1 = [](auto t){
    std::cout << t << std::endl;
  };
  // tpl1 の各要素に f1 を適用。
  ApplyTuple(f1, tpl1, std::make_index_sequence<size1>());

  /** 戻り値を受け取る場合 */
  // タプルを生成。
  auto tpl2 = std::make_tuple(1, 1.2);
  // タプルのサイズを取得。
  constexpr size_t size2 = std::tuple_size<decltype(tpl2)>::value;
  // タプルの要素に適用する関数。ここでは、要素に 1 を足した結果を返す。
  auto f2 = [](auto t){
    return t + 1;
  };
  // 戻り値の型を指定。
  using Rv = std::tuple<int, double>;
  // 戻り値の型を指定して、tupl2 の各要素に f2 を適用。
  auto rv = ApplyTuple<Rv>(f2, tpl2, std::make_index_sequence<size2>());
  // 結果の確認
  std::cout << std::get<0>(rv) << std::endl; //=> 2
  std::cout << std::get<1>(rv) << std::endl; //=> 2.2

  return 0;
};
```

タプルの各要素に関数を適用した結果を、

1. 受け取らない場合
2. タイプ型として受け取る場合

の 2 種類用意しました。

## タプルの要素への副作用を期待する場合の注意点

タプルへの副作用を期待して、ラムダ式の引数をコピー渡し `auto` から参照渡し `auto&` にする場合、注意が必要です。

というのも、タプルの要素は const 修飾されており、そのままでは値を変更することができないからです。

自作クラスのインスタンスを要素に持つタプルを逐次処理して、タプルの要素である各インスタンスにメンバー関数を順次適用させていく場合には、自作クラスのメンバー変数を mutable 修飾したり、メンバー関数を const 修飾したりする必要があります。

コードで見てみます。

```C++
// ...
// 上に ApplyTuple 等のコード

template <typename T>
class A {
public:
  // const メンバー関数から値を変更するメンバー変数には mutable 修飾が必要。
  mutable T value_;

  // const 修飾されたインスタンスに適用させたいメンバー関数には const 修飾が必要。
  void Add(const T val) const {
    value_ += val;
  };
};

int main(void) {
  auto a1 = A<int>();
  a1.value_ = 1;

  auto a2 = A<double>();
  a2.value_ = 1.2;

  // tpl の要素は const 修飾されている。
  auto tpl = std::make_tuple(a1, a2);
  constexpr size_t size = std::tuple_size<decltype(tpl)>::value;
  // 参照渡し auto& にしている。
  auto f = [](auto& a) {
    a.Add(1);
  };
  ApplyTuple(f, tpl, std::make_index_sequence<size>());

  std::cout << std::get<0>(tpl).value_ << std::endl; //=> 2
  std::cout << std::get<1>(tpl).value_ << std::endl; //=> 2.2

  return 0;
};
```

## 補足

個人的には、以下の違いが勉強になりました。

* エラーになる例

```C++
// 整数が indice に実行時パラメータとして渡り、エラーになる。
template <typename T, typename... Indices>
void ApplyTuple(T tpl, Indices... indices) {
  using swallow = int[];
  (void)swallow {
    (std::cout << std::get<indices>(tpl) << std::endl, 0)... 
  };
};

int main(void) {
  auto tpl = std::make_tuple("abc", 1, 1.2);
  constexpr size_t size = std::tuple_size<decltype(tpl)>::value;
  ApplyTuple(tpl, 0, 1, 2);
  return 0;
}
```

* エラーにならない例

```C++
// 整数が indices に非型テンプレートパラメータとして渡り、エラーにならない。
template <typename T, std::size_t... indices>
void ApplyTuple(T tpl, std::index_sequence<indices...>) {
  using swallow = int[];
  (void)swallow {
    (std::cout << std::get<indices>(tpl) << std::endl, 0)... 
  };
};

int main(void) {
  auto tpl = std::make_tuple("abc", 1, 1.2);
  constexpr size_t size = std::tuple_size<decltype(tpl)>::value;
  ApplyTuple(tpl, std::make_index_sequence<size>());
  return 0;
}
```

## 参考

* [第20回 std::tupleの要素を静的に枚挙する](https://theolizer.com/cpp-school2/cpp-school2-20/)
* [C++のパラメータパック基礎&パック展開テクニック](https://qiita.com/_EnumHack/items/677363eec054d70b298d)
* [std::tupleの全要素の走査](https://blog.browniealice.net/post/tuple_iterate/)