第50回 可変長テンプレート (Variadic Template)
関数や型に任意個の引数を渡せる仕組み。printf の型安全版、std::make_shared、vector::emplace_back など、標準ライブラリの便利機能はこれで作られています。
このページで押さえること
✅ 最低限ここだけ覚える
template<class... Args> で可変長
- 関数で
Args... args として受け取り、args... で転送
- C++17 の fold expression
(... + args)
- emplace や make_unique/shared で日常的に恩恵
⭐ 余裕があれば読む
- 再帰展開(古い書き方)
- sizeof...(Args)
- perfect forwarding との組合せ
- 型リストメタプログラミング
1. 基本 ― 合計関数
昔の書き方再帰展開
// 終了条件
int sum() { return 0; }
// 再帰展開
template<class T, class... Rest>
T sum(T first, Rest... rest) {
return first + sum(rest...);
}
sum(1, 2, 3, 4); // 10
C++17 fold簡潔
template<class... Args>
auto sum(Args... args) {
return (... + args); // fold expression
}
sum(1, 2, 3, 4); // 10
sum(1.0, 2, 3.5); // 6.5 (混在も OK)
(... + args) はfold expression。引数を args[0] + args[1] + args[2] + ... のように展開します。
2. パラメータパックの基本
class... Args ― テンプレートパラメータパック(型のリスト)
Args... args ― 関数パラメータパック(引数のリスト)
args... ― パック展開(使う場所で展開する)
sizeof...(args) ― 引数の個数
転送する(perfect forwarding)STL の定番
template<class T, class... Args>
std::unique_ptr<T> make_unique_like(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// std::make_unique の実装そのもの
std::forward と組み合わせると、受け取った引数を左辺値/右辺値の区別を保ったまま別関数に渡せます。これが emplace 系関数の仕組み。
3. C++17 fold expression
単項 fold演算子のみ
template<class... Args>
auto sum(Args... a) {
return (... + a); // a0+a1+a2+...
}
template<class... Args>
auto all(Args... a) {
return (... && a); // a0 && a1 && ...
}
all(true, true, false); // false
二項 fold初期値あり
template<class... Args>
auto sum(Args... a) {
return (0 + ... + a); // 初期値 0
}
sum(); // 0(引数なしでも動く)
// 出力を複数個並べる fold もよくある
template<class... Args>
void print_all(Args... a) {
((std::cout << a << " "), ...); // カンマ演算子で並列
}
4. 日常で気づかず使う例
std::make_unique<T>(args...) ― 任意のコンストラクタに転送
std::make_shared<T>(args...) ― 同上
vector.emplace_back(args...) ― その場で構築
std::tuple<Args...> ― 任意個の型をまとめる
普段は意識しないが: emplace_back(1, "hello") のように複数引数を取れるのは variadic template のおかげ。内部で new T(1, "hello") に転送されています。
⭐
ここまでで基本は OK
STEP 8 は完了。次は STL アルゴリズム特集(目玉 4 章)へ。
確認クイズ
可変長テンプレートを 3 問で確認。
Q1. 可変長テンプレートのパラメータパック宣言として正しいのは?
template<class T[]>
template<class T*>
template<class... Args>
template<class ...Args> (スペース重要)
class... Args で任意個の型パラメータ。関数引数では Args... args、使う場所では args... で展開。
Q2. C++17 の fold expression (... + args) の役割は?
配列を初期化
パラメータパックを演算子で畳み込む(a0 + a1 + ...)
関数オーバーロード
可変長引数を配列化
fold expression は C++17 で導入。パックを指定した演算子で畳み込む。以前は再帰展開でゴリ押しだった処理が劇的に簡潔になりました。
Q3. std::make_unique<T>(1, "a", 3.14) のような任意個数の引数を受け取る実装の根幹は?
オーバーロード
デフォルト引数
可変長テンプレート + perfect forwarding
va_list マクロ
variadic template + std::forward で引数を型情報を保ったまま転送。これが emplace 系と make_unique/make_shared の仕組み。