c++の可変長テンプレートで2つの型リストKeysとValuesを使いたいとする
その場合は
- リストの片方は std::tupleで一つの型にする
- 残りのリストは 可変長テンプレートにする
とコーディングしやすい
例
template <typename KeyTuple, typename... Values_> struct Foo { using Keys = KeyTuple; using Values = std::tuple<Values_...>; };
std:tuple で指定するリストは,長さが1以上のリスト,つまり要素を省略できないリストを選ぶと良い
これで
Foo<std::tuple<int, std::string>, bool> foo1; Foo<std::tuple<int, std::string>> foo2; Foo<std::tuple<int, std::string>, bool, int, float> foo3;
といったコードが書けるようになる
解説
可変長テンプレートを2つ指定することは可能で,以下のコードはコンパイルできる
template <typename... Keys_, typename... Values_> struct Foo {}; Foo<int, double, std::string, char, bool> foo;
しかしこれでは,どこまでがKeysで,どこからが Valuesが区別できない.
template <typename KeyTuple, typename... Values_> struct Foo { using Keys = KeyTyple; using Values = std::tuple<Values_...>; }; Foo<std::tuple<int, double, std::string>, char, bool> foo;
と書けば Values は charとboolの2つであることがわかる
内部的には Foo::KeysとFoo::Valuesはどちらも std::tuple なので,可変長テンプレートの恩恵は等しく受けられる
例えば型リストの長さを取る場合
template <typename KeyTuple, typename... Values_> struct Foo { using Keys = KeyTyple; using Values = std::tuple<Values_...>; static constexpr std::size_t KeyCount = std::tuple_size_v<Keys>; static constexpr std::size_t ValueCount = std::tuple_size_v<Values>; };
keysをすべて std::cout に出力する場合
template <typename KeyTuple, typename... Values_> struct Foo { using Keys = KeyTyple; using Values = std::tuple<Values_...>; std::string keys_string() const { std::string oss; std::apply([&oss](const auto&... e) { size_t n=0; ((oss << (n++?",":"") << e), ...); }, keys); return oss.str(); } }; Foo<std::tuple<int,std::string>, int> foo; std::cout << foo.keys_string() << std::endl;
さらなる拡張
c++20の concepts を使うとより使いやすくなる
#include <tuple> #include <type_traits> #include <iostream> template <typename T> concept TupleType = requires { typename std::tuple_size<T>::type; }; template <TupleType KeyTuple, typename... Values_>
これで
Foo<std::string, char, bool> foo;
のような間違ったコードをコンパイル時エラーにできる