Variadic templates (C++11) ช่วยให้เขียนฟังก์ชัน/คลาสที่รับจำนวนอาร์กิวเมนต์ไม่จำกัดได้ แทนที่ ellipsis (...) แบบ C-style ที่ไม่ปลอดภัย
Syntax พื้นฐาน
template <typename T, typename... Args> return_type function_name(T first, Args... args);
typename... Args: parameter pack (แพ็คของชนิด)Args... args: parameter pack expansion (แพ็คของค่า)sizeof...(Args): จำนวนอาร์กิวเมนต์ในแพ็ค
รูปแบบการใช้งานทั่วไป: Recursive Unpacking
#include <iostream>
#include <string>
using namespace std;
// Base case: ไม่มีอาร์กิวเมนต์เหลือ
void printAll() {
cout << "--- จบรายการ ---" << endl;
}
// Recursive case: ประมวลผลตัวแรก แล้วเรียกตัวเองกับที่เหลือ
template <typename T, typename... Args>
void printAll(T first, Args... args) {
cout << first << " ";
printAll(args...); // pack expansion
}
int main() {
printAll(7, 6, 0.04, "hello", "variadic templates", '\n');
// Output: 7 6 0.04 hello variadic templates
// --- จบรายการ ---
return 0;
}
ผลลัพธ์
7 6 0.04 hello variadic templates --- จบรายการ ---
เทคนิคขั้นสูง
1. Fold Expressions (C++17) - ไม่ต้อง recursion
// ผลรวม
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // unary right fold
}
// พิมพ์ทั้งหมด (C++17)
template <typename... Args>
void printFold(Args... args) {
(cout << ... << args) << endl; // fold over comma operator
}
// นับจำนวนอาร์กิวเมนต์
template <typename... Args>
constexpr size_t countArgs(Args...) {
return sizeof...(Args);
}
2. Perfect Forwarding
template <typename Func, typename... Args>
decltype(auto) invoke(Func&& f, Args&&... args) {
return std::forward<Func>(f)(std::forward<Args>(args)...);
}
// ใช้งาน
invoke([](int a, double b) { return a + b; }, 10, 3.14); // 13.14
3. Tuple/Parameter Pack Manipulation
#include <tuple>
#include <utility>
// แปลง parameter pack เป็น tuple
template <typename... Args>
auto makeTuple(Args&&... args) {
return std::make_tuple(std::forward<Args>(args)...);
}
// ใช้ index_sequence สำหรับการ unpack แบบควบคุมได้
template <typename Tuple, size_t... I>
void printTupleImpl(const Tuple& t, std::index_sequence<I...>) {
(cout << ... << std::get<I>(t) << " ") << endl;
}
template <typename... Args>
void printTuple(Args&&... args) {
printTupleImpl(std::make_tuple(std::forward<Args>(args)...),
std::index_sequence_for<Args...>{});
}
ตัวอย่างการใช้งานจริง: Logger
#include <iostream>
#include <chrono>
#include <format> // C++20
enum class LogLevel { INFO, WARN, ERROR };
template <LogLevel level, typename... Args>
void log(Args&&... args) {
auto now = std::chrono::system_clock::now();
// C++20: auto timeStr = std::format("{:%H:%M:%S}", now);
const char* levelStr = (level == LogLevel::INFO) ? "[INFO] " :
(level == LogLevel::WARN) ? "[WARN] " : "[ERROR]";
cout << levelStr;
(cout << ... << std::forward<Args>(args)) << endl; // C++17 fold
}
int main() {
log<LogLevel::INFO>("Server started on port ", 8080);
log<LogLevel::WARN>("High memory usage: ", 85, "%");
log<LogLevel::ERROR>("Connection failed: ", "timeout");
}
สรุป
- Variadic templates = type-safe, flexible, compile-time parameter packs
- C++11: ใช้ recursion unpacking
- C++17: ใช้ fold expressions (ง่ายกว่า เร็วกว่า)
- C++20: ใช้
std::format, concepts ปรับปรุงความปลอดภัย