본문으로 건너뛰기 Advanced C++ Variadic Templates

Advanced C++ Variadic Templates

Advanced C++ Variadic Templates

이 글의 핵심

Deep dive into variadic templates: parameter packs, expansion patterns, C++17 folds, and practical tips for logging, type lists, and perfect forwarding.

Advanced variadic templates

A variadic template accepts a variable number of types or values. You pack them with ...Args, then unpack with recursion or fold expressions (C++17). Pair this with template basics and template specialization for stronger designs.

Packing and recursion

To accept variadic arguments, declare a parameter pack and split work between a base case (zero arguments) and a recursive case (first argument + rest of the pack).

#include <iostream>
// Base case
// 실행 예제
void print() {
    std::cout << '\n';
}
// Recursive case
template<typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << ' ';
    print(rest...);  // expand rest and recurse
}
int main() {
    print(1, 2.0, "three");
    // output: 1 2 three
    return 0;
}

How it works:

  1. print(1, 2.0, "three"): T = int, Rest = {double, const char*}
  2. Print 1, then print(2.0, "three")
  3. Continue until Rest is empty, then print() (base) Tip: Rest... is “the remaining types”; rest... expands the remaining arguments for the next print call. A fold expression can replace this recursion in one line.
template<typename... Args>
void print_fold(Args... args) {
    (std::cout << ... << args) << '\n';
}
print_fold(1, 2.0, "three");  // no explicit recursion

Fold expressions (C++17)

A fold applies a binary operator across the whole pack. (args + ...) is a unary right fold, equivalent to args_1 + (args_2 + (args_3 + ...)).

template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // unary right fold
}
template<typename... Args>
bool all(Args... args) {
    return (args && ...);
}
// Empty pack: use a binary fold with an initializer
template<typename... Args>
auto sum_with_zero(Args... args) {
    return (0 + ... + args);  // empty pack => 0
}

Use cases include logging, small utility wrappers, and type-list processing.

Pack expansion patterns

Expanding a pack as (expr)... repeats expr for each element.

#include <iostream>
#include <vector>
template<typename... Args>
void call_functions(Args... args) {
    (process(args), ...);  // process(arg1), process(arg2), ...
}
template<typename... Args>
void add_to_vector(std::vector<int>& v, Args... args) {
    (v.push_back(args), ...);
}
template<typename... Args>
void log(Args&&... args) {
    (std::cout << ... << args) << '\n';
}

Expansion patterns:

PatternMeaningExample
args...Unpack packf(args...)f(a1, a2, a3)
(expr(args))...Per-element expression(f(args))...
(args + ...)Unary right fold
(... + args)Unary left fold
(init + ... + args)Binary fold
Examples:
template<typename... Args>
void set_values(int& result, Args... args) {
    result = (args + ...);
}
template<typename... Args>
bool all_true(Args... args) {
    return (args && ...);
}
template<typename... Funcs>
void execute_all(Funcs... funcs) {
    (funcs(), ...);
}

Example: type-safe tuple indexing idea

You can build a metafunction that returns the type at index I from a type list—useful with structured bindings.

template<typename... Ts>
struct type_list {};
template<size_t I, typename T>
struct type_at_impl;
template<typename T, typename... Rest>
struct type_at_impl<0, type_list<T, Rest...>> {
    using type = T;
};
template<size_t I, typename T, typename... Rest>
struct type_at_impl<I, type_list<T, Rest...>> {
    using type = typename type_at_impl<I - 1, type_list<Rest...>>::type;
};

Common issues

  • Empty packs: Some operators are ill-formed on empty unary folds; use a binary fold with an initializer or if constexpr (sizeof...(args) > 0).
  • Expansion context: (args)... vs (args...) differ—parentheses group what repeats.

Summary

TopicNotes
Parameter packtypename... Args, Args... args
Fold(args op ...), (... op args), (init op ... op args)
PracticeLogging, wrappers, type lists, variadic adapters
In one sentence: Variadic templates let you write APIs independent of arity; C++17 folds often replace recursion.

Keywords

C++, variadic template, parameter pack, fold expression, C++17.

Checklist

Before coding

  • Is this the best approach?
  • Can the team maintain it?
  • Does it meet performance goals?

While coding

  • Warnings addressed?
  • Edge cases covered?
  • Errors handled?

At review

  • Clear intent?
  • Enough tests?
  • Documented?

FAQ

Q. Where is this used in production?

A. Logging, generic adapters, type lists, and any API that must accept a variable number of types or values—see the patterns above.

Q. What should I read first?

A. Follow Related posts at the bottom of each article, or use the C++ series index.

Q. Go deeper?

A. cppreference and library docs are the authoritative references.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, Variadic Template, Parameter Pack, Fold Expression, C++17.