Hầu hết các ví dụ cho đến nay đã hoạt động trên các giá trị (số máy tính của pi, giai thừa của N hoặc tương tự), và đó là khá nhiều sách giáo khoa, nhưng chúng thường không hữu ích. Thật khó để tưởng tượng một tình huống mà bạn thực sự cần trình biên dịch để tính toán chữ số thứ 17 của pi. Hoặc bạn tự mã hóa nó, hoặc tính toán nó khi chạy.
Một ví dụ mà có thể phù hợp hơn với thế giới thực có thể là thế này:
Hãy nói rằng chúng ta có một lớp mảng mà kích thước là một tham số mẫu (vì vậy đây sẽ khai báo một mảng của 10 số nguyên: array<int, 10>
)
Bây giờ chúng tôi có thể muốn nối hai mảng và chúng tôi có thể sử dụng một chút lập trình meta để tính toán kích thước mảng kết quả.
template <typename T, int lhs_size, int rhs_size>
array<T, lhs_size + rhs_size> concat(const array<T, lhs_size>& lhs, const array<T, rhs_size>& rhs){
array<T, lhs_size + rhs_size> result;
// copy values from lhs and rhs to result
return result;
}
Một ví dụ rất đơn giản, nhưng ít nhất các loại có liên quan thực tế. Hàm này tạo ra một mảng có kích thước chính xác, nó làm như vậy tại thời gian biên dịch, và với toàn bộ kiểu an toàn. Và đó là tính toán thứ gì đó mà chúng tôi không thể thực hiện bằng cách mã hóa các giá trị (chúng tôi có thể muốn ghép nhiều mảng với kích thước khác nhau) hoặc khi chạy (vì sau đó chúng tôi sẽ mất thông tin loại)
Phổ biến hơn, mặc dù, bạn có xu hướng sử dụng metaprogramming cho các loại, chứ không phải là giá trị.
Một ví dụ điển hình có thể được tìm thấy trong thư viện chuẩn. Mỗi loại vùng chứa định nghĩa loại trình lặp riêng của nó, nhưng các con trỏ cũ đơn giản có thể cũng là được sử dụng làm trình lặp. Về mặt kỹ thuật, một trình vòng lặp được yêu cầu để hiển thị một số thành viên typedef, chẳng hạn như value_type
và các con trỏ rõ ràng là không làm điều đó. Vì vậy, chúng tôi sử dụng một chút lập trình meta để nói "oh, nhưng nếu loại trình biến đổi hóa ra là một con trỏ, thì thay vào đó, định dạng của nó sẽ sử dụng định nghĩa này."
Có hai điều cần lưu ý về điều này. Đầu tiên là chúng ta đang thao tác các loại, không phải giá trị Chúng ta không nói "giai thừa của N là như vậy và như vậy", nhưng đúng hơn, "value_type
của một loại T được định nghĩa là ..."
điều thứ hai là nó được sử dụng để tạo điều kiện lập trình chung. (Iterator sẽ không phải là một khái niệm rất chung chung nếu nó không hoạt động cho các ví dụ đơn giản nhất, một con trỏ vào một mảng. Vì vậy, chúng tôi sử dụng một chút lập trình meta để điền vào các chi tiết cần thiết cho một con trỏ được coi là hợp lệ trình lặp).
Đây là trường hợp sử dụng khá phổ biến cho lập trình meta. Chắc chắn, bạn có thể sử dụng nó cho một loạt các mục đích khác (mẫu Expression là một ví dụ thường được sử dụng, nhằm tối ưu hóa các tính toán đắt tiền, và Boost.Spirit là một ví dụ về việc hoàn toàn overboard và cho phép bạn định nghĩa trình phân tích cú pháp của riêng bạn tại compile- thời gian), nhưng có lẽ cách sử dụng phổ biến nhất là làm mịn những va chạm nhỏ và các trường hợp góc mà nếu không sẽ yêu cầu xử lý đặc biệt và làm cho chương trình chung không thể.
Tôi không phải là lập trình viên QT, nhưng nó trông giống như một hình thức lập trình meta. Tuy nhiên, khi lập trình viên C++ nói về metaprogramming, họ thường đề cập đến lập trình meta template cụ thể (không liên quan gì đến hệ thống meta-object của QT). Một số ngôn ngữ khác tồn tại: Một số ngôn ngữ cho phép bạn lập trình meta tại thời gian chạy (thao tác hoặc mở rộng các loại trong khi chương trình đang chạy), và cách tiếp cận của QT dường như tạo ra các tệp mã mới để đượC#included. Nhưng đó là tất cả metaprogramming, trong đó một metaprogram chỉ đơn giản là một chương trình mà tạo ra, hoặc thao tác một chương trình. – jalf
Bạn có quan tâm đến lập trình meta tổng quát hay chỉ là lập trình meta mẫu C++? –