Khuôn mẫu Variadic được giới thiệu trong C++11. Khuôn mẫu Variadic cho phép một hàm nhận bất kỳ kiểu đối số nào. Hãy tìm hiểu bằng một ví dụ sau.
Giả sử chúng ta muốn tạo một hàm log() chấp nhận số lượng đối số thay đổi của bất kỳ kiểu nào và in chúng trên bảng điều khiển (console), tức là
log(1,4.3, "Hello");
log('a', "test", 78L, 5);
class Student;
Student obj;
log(3, obj);
etc..
Nó có nghĩa là chúng ta có 2 yêu cầu khi tạo hàm log(),
- Nó phải chấp nhận bất kỳ loại đối số nào.
- Nó phải chấp nhận số lượng đối số thay đổi.
Theo yêu cầu 1, đối số có thể thuộc bất kỳ loại nào. Vì vậy, rõ ràng là chúng ta cần tạo một hàm khuôn mẫu, một cái gì đó như thế này,
template<typename T>
void log(T obj)
{
std::cout<<obj;
}
Nhưng hàm này chỉ chấp nhận một đối số. Để thực hiện đầy đủ yêu cầu thứ 2, chúng ta cần tạo một hàm chấp nhận số lượng tham số mẫu thay đổi. Tính năng này là khuôn mẫu Variadic.
Tạo hàm chấp nhận số lượng đối số thay đổi của bất kỳ kiểu nào
Với khuôn mẫu Variadic, chúng ta có thể xác định một hàm chấp nhận một lượng số tham số mẫu thay đổi, tức là
template<typename T, typename ... Args>
void log(T first, Args ... args);
Hàm trên có thể nhận một hoặc nhiều đối số. Ở đây, Args… đại diện cho số lượng tham số mẫu thay đổi. Việc khai báo hàm mẫu Variadic rất dễ dàng nhưng định nghĩa của nó hơi phức tạp. Chúng ta không thể truy cập trực tiếp vào số biến tham số đã truyền. Chúng ta cần sử dụng cơ chế diễn dịch kiểu & đệ quy để đạt được điều này. Định nghĩa của một hàm Variadic có thể như sau,
/*
* Variadic Template Function that accepts variable number
* of arguments of any type.
*/
template<typename T, typename ... Args>
void log(T first, Args ... args) {
// Print the First Element
std::cout<<first<<" , ";
// Forward the remaining arguments
log(args ...);
}
Cơ chế hoạt động
Hãy xem nó hoạt động như thế nào. Khi chúng ta đã gọi hàm log(),
log(2, 3.4 , "aaa");
Vì chúng ta đã truyền ba đối số kiểu int, double và const char *. Cho nên, bằng cách sử dụng cơ chế diễn dịch kiểu, trình biên dịch sẽ tạo ra hàm sau từ hàm mẫu Variadic đã cho, tức là
void log(int first, double b, const char * c)
{
std::cout<<first<<" , ";
log(b, c);
}
Nó sẽ in tham số đầu tiên và chuyển tiếp hai tham số còn lại đến một hàm log khác. Bây giờ tiếp tục sử dụng cơ chế diễn dịch kiểu, trình biên dịch sẽ tạo ra hàm sau từ hàm mẫu Variadic đã cho,
void log(double first, const char * c)
{
std::cout<<first<<" , ";
log(c);
}
Tiếp tục, nó sẽ in tham số đầu tiên và chuyển tiếp tham số còn lại đến một hàm log khác. Sử dụng tiếp cơ chế diễn dịch kiểu, trình biên dịch sẽ lại tạo ra hàm sau từ hàm mẫu Variadic đã cho,
void log(const char * first)
{
std::cout<<first<<" , ";
log();
}
Nó sẽ in tham số đầu tiên và vì không nhận được đối số nào khác, nó sẽ gọi hàm log() không có tham số, tức là trả về sau đó. Đó là lý do tại sao ta cần định nghĩa một hàm log() không có tham số để dừng đệ quy.
Như vậy, đây sẽ là ngăn xếp của việc gọi hàm mẫu Variadic ở trên,
void log();
void log(const char * first);
void log(double first, const char * c);
void log(int first, double b, const char * c);
Ví dụ đầy đủ như sau,
#include <iostream>
// Function that accepts no parameter
// It is to break the recursion chain of vardiac template function
void log()
{
}
/*
* Variadic Template Function that accepts variable number
* of arguments of any type.
*/
template<typename T, typename ... Args>
void log(T first, Args ... args) {
// Print the First Element
std::cout<<first<<" , ";
// Forward the remaining arguments
log(args ...);
}
int main() {
log(2, 3.4, "aaa");
return 0;
}
Kết quả khi in ra sẽ là: 2 , 3.4 , aaa ,
Để lại một bình luận