close up photo of programming of codes

codecungnhau.com

Một trang web về kỹ thuật lập trình

C++11 – Khuôn mẫu hàm Variadic (Variadic Template)

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 ,


Đã đăng vào

trong

,

bởi

Thẻ:

Bình luận

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *