1. Vấn đề nó giải quyết
Lặp cùng một thuật toán cho nhiều kiểu tạo ra code trùng. Template mô tả cả họ khai báo, parameter pack nhận số lượng template argument thay đổi, type trait kiểm tra kiểu, còn constexpr cho phép tính toán trong lúc biên dịch.
2. Kiến thức cần có
Ngày 1-4; hàm, overload, đệ quy,
autovà hằng số compile-time của C++11.
3. Ý tưởng cốt lõi
Template là công thức ở compile-time. Khi instantiate, compiler thay kiểu cụ thể, trait cung cấp thông tin Boolean về kiểu và lời gọi constant expression có thể được tính trước khi chương trình chạy.
4. Cú pháp tối thiểu
template<class T, class... Rest>
constexpr T sum(T first, Rest... rest);5. Cách nó hoạt động
Mỗi lần gọi suy ra các kiểu argument cụ thể và bung pack thành lời gọi đệ quy nhỏ hơn.
Type trait kiểm tra kiểu kết quả, còn
constexprcho phép compiler dùng kết quả trongstatic_assert.Cùng một định nghĩa cộng được nhiều giá trị nguyên và chứng minh kết quả mong đợi ngay lúc biên dịch.
6. Lỗi thường gặp
Bung pack mà không có overload dừng hoặc trộn các kiểu không tương thích có thể tạo thông báo lỗi rất dài.
Trước khi áp dụng mẫu, phải kiểm tra base case, common type được suy ra, điều kiện trait và giới hạn của constant expression.
7. Khi nào nên dùng
Nên dùng khi một thao tác thực sự áp dụng cho nhiều kiểu hoặc số argument khác nhau và kiểm tra compile-time đem lại ích lợi.
Tránh dùng khi overload thông thường rõ hơn hoặc lỗi template phức tạp hơn lợi ích tái sử dụng.
8. Ví dụ đơn giản
Hàm đệ quy nhỏ sum nhận ba số nguyên. std::common_type chọn kiểu kết quả phù hợp và static_assert kiểm tra đáp án trước runtime.
File .cpp dùng dữ liệu cố định để tự đoán và kiểm tra output.
Mã mẫu hoàn chỉnh
Tệp mã nguồn
cpp14/05_templates_parameter_packs_type_traits_constexpr_review/main.cpp
#include <iostream>
#include <type_traits>
template <class T>
constexpr T sum(T value) {
return value;
}
template <class T, class... Rest>
constexpr typename std::common_type<T, Rest...>::type
sum(T first, Rest... rest) {
return first + sum(rest...);
}
int main() {
constexpr auto total = sum(1, 2, 3, 4);
static_assert(std::is_integral<decltype(total)>::value, "integral result");
static_assert(total == 10, "compile-time sum");
std::cout << "sum: " << total << "\n";
}
9. Điều cần nhớ
Trừu tượng compile-time hữu ích khi constraint và quy tắc kết thúc vẫn nhìn thấy rõ.
Compiler hoặc thư viện luôn theo quy tắc cụ thể; cần kiểm tra base case, common type được suy ra, điều kiện trait và giới hạn của constant expression.
Ưu tiên cách viết nhỏ nhất thể hiện rõ ý định và đo đạc khi hiệu năng thực sự quan trọng.
10. Câu hỏi tự kiểm tra
Dễ — Mục đích chính của Ôn templates, parameter packs, type traits và constexpr là gì?
Trung bình — Sau khi gọi
sum(1, 2, 3)và đi tới base case một argument, đã có bao nhiêu bước đệ quy?Khó — Vì sao kiểu trả về phải tính đến mọi phần tử trong pack thay vì chỉ dùng kiểu của argument đầu?