Bỏ qua để đến nội dung

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 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ệu
  • Rest... rest: “gói tham số giá trị” - đại diện cho danh sách các đối số tương ứng
  • rest...: “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;
}
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…)
  • 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