Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

Variadic Function Templates ใน C++: ฟังก์ชันรับอาร์กิวเมนต์ไม่จำกัด

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 ปรับปรุงความปลอดภัย