Hàm với số lượng tham số biến đổi
Đôi khi bạn cần một hàm nhận số lượng tham số không cố định - ví dụ một hàm tính tổng của 2, 3, hay 10 số. C++ có vài cách tiếp cận cho vấn đề này, từ đơn giản đến hiện đại.
Cách 1: dùng std::initializer_list
Phần tiêu đề “Cách 1: dùng std::initializer_list”Cách phổ biến và an toàn nhất khi các tham số cùng kiểu dữ liệu:
#include <initializer_list>#include <iostream>
int sum(std::initializer_list<int> numbers) { int total = 0; for (int n : numbers) { total += n; } return total;}
int main() { std::cout << sum({1, 2, 3}) << std::endl; // 6 std::cout << sum({1, 2, 3, 4, 5}) << std::endl; // 15 std::cout << sum({}) << std::endl; // 0
return 0;}Cú pháp gọi hàm sum({1, 2, 3}) với dấu ngoặc nhọn khá gần với cách truyền một danh sách giá trị trong nhiều ngôn ngữ khác.
Cách 2: Variadic template (nhận nhiều kiểu dữ liệu khác nhau)
Phần tiêu đề “Cách 2: Variadic template (nhận nhiều kiểu dữ liệu khác nhau)”Khi các tham số có thể khác kiểu dữ liệu, dùng variadic template (C++11 trở lên) kết hợp với tính năng “gói tham số” (parameter pack):
#include <iostream>
void print_all() { std::cout << std::endl; // Trường hợp dừng đệ quy: không còn tham số nào}
template <typename T, typename... Rest>void print_all(T first, Rest... rest) { std::cout << first << " "; print_all(rest...); // Gọi đệ quy với phần còn lại của tham số}
int main() { print_all(1, "Hello", 3.14, 'A'); // In ra: 1 Hello 3.14 A
return 0;}typename... Rest: “gói tham số kiểu” - đại diện cho không hoặc nhiều kiểu dữ liệuRest... rest: “gói tham số giá trị” - đại diện cho danh sách các đối số tương ứngrest...: “mở gói” khi truyền tiếp cho lời gọi đệ quy
Đây là kỹ thuật nâng cao, thường gặp khi đọc code thư viện hơn là viết hàng ngày, nhưng hiểu được cách hoạt động cơ bản của nó rất hữu ích.
Cách 3: Fold expression (C++17) - đơn giản hóa variadic template
Phần tiêu đề “Cách 3: Fold expression (C++17) - đơn giản hóa variadic template”C++17 giới thiệu fold expression, giúp viết các hàm variadic đơn giản (như tính tổng) ngắn gọn hơn nhiều, không cần đệ quy:
#include <iostream>
template <typename... Args>auto sum_all(Args... args) { return (args + ...); // Fold expression: cộng dồn tất cả tham số}
int main() { std::cout << sum_all(1, 2, 3) << std::endl; // 6 std::cout << sum_all(1.5, 2.5, 3.0) << std::endl; // 7
return 0;}So sánh các cách tiếp cận
Phần tiêu đề “So sánh các cách tiếp cận”| Cách | Khi nào dùng |
|---|---|
std::initializer_list<T> |
Số lượng tham số thay đổi, nhưng cùng một kiểu dữ liệu |
| Variadic template | Số lượng tham số thay đổi, khác kiểu dữ liệu, cần xử lý logic phức tạp |
| Fold expression (C++17) | Variadic template cho các phép toán đơn giản (cộng, nối chuỗi…) |
Tổng kết
Phần tiêu đề “Tổng kết”std::initializer_list<T>phù hợp khi các tham số biến đổi về số lượng nhưng cùng kiểu dữ liệu- Variadic template (
typename... Args) cho phép nhận số lượng tham số bất kỳ với kiểu dữ liệu khác nhau - Fold expression (C++17) đơn giản hóa nhiều tình huống dùng variadic template phổ biến như tính tổng