Trong bài này mình sẽ giới thiệu cho các bạn một specifier rất hữu ích trong C++ để tối ưu code tại compile-time.
Giới thiệu constexpr
Như các bạn đã biết, một trong những điểm mạnh mẽ của C++ là nó khả năng tối ưu mã nguồn ngay tại thời điểm compile hay còn gọi là compile-time evaluation. Trong quá trình biên dịch, compiler có thể tính toán được output của functions, variables và lưu dưới dạng ngôn ngữ máy (code machine), sau đó khi chạy thì chương trình không cần phải tính toán lại functions đó nữa, điều này rất tiết kiệm thời gian khi runtime, nhưng chỉ áp dụng cho các funtions là pure function (biết trước các argurments tại compile-time, không có đầu vào là reference, không modify bất giá global object nào)
Vậy constexpr là gì?
constexpr specifier được giới thiệu từ C++ 11, nó có thể được gắn trước một đối tượng hoặc hàm với ngụ ý rằng giá trị trả về của đối tượng hoặc hàm có thể được biết tại thời điểm biên dịch.
Cú pháp:
constexpr literal-type identifier = constant-expression ;constexpr literal-type identifier { constant-expression } ;constexpr literal-type identifier ( params ) ;constexpr ctor ( params ) ;
Nó khác gì const?
Không giống như const, constexpr cũng có thể được áp dụng cho các function và constructor. constexpr cho biết rằng giá trị hoặc giá trị trả về là const và nếu có thể sẽ được tính toán tại thời điểm biên dịch.
Cách sử dụng và kiểm chứng
Constexpr variables

Như các bạn đã thấy, khi biên dịch được code sẽ bị lỗi ngay tại thời điểm compile. Vì khi constexpr thì compiler hiểu rằng giá trị đó phải được tính tại thời điểm biển dịch chương trình nên line 4 và 6 sẽ lỗi do chưa được initialized và chỉ thực hiện các toán tử với const.
Constexpr function
Hãy thử với function bình thường trước nhé, như các bạn thấy bên phải là assembly code, lúc này compiler đã không thể tính toán được output của function mặc dù đã biết trước hai tham số là 5 và 6.

Giờ hãy thêm constexpr specifier vào nhé:

Wow, compiler đã tối ưu rất nhiều, và như mỗi tên mình chỉ, nó đã tính được output của function là 11 ngay tại thời điểm compile-time. Sự khác biệt là đây đúng không nào, giả sử function của chúng phức tạp hơn, có khối lượng tính toán nhiều hơn mà lại được compiler tính ngay tại thời compile thì sẽ cải thiện hiệu năng đáng kể khi runtime.
Constexpr for recursive function
Câu hỏi mà nhiều bạn nghi ngờ là liệu recursive function thì có thể tính kết quả tại compile-time được không?

Câu trời là có nhé, hãy thử với recursive function tính giai thừa nhé:
Như các bạn thấy, compiler toàn hoàn có thể tính được output của recursive function đúng không nào.
Điều cần chú với recursive function là nó rất dễ xảy ra stack oveflow, vậy nên C++ cũng có giới hạn recursive constexpr calls. Chúng ta có thể điều chỉnh giá trị này bằng cách thay đổi cờ -fconstexpr-depth=2, ví dụ ở đây mình gán depth recursion tại compile time bằng 2 và biên dịch là, thì chương trình trên sẽ lỗi do constexpr calls vượt quá.

Không phải lúc nào áp dụng constexpr thì function sẽ được tính toán ngay tại thời điểm compile, nó phải theo các yêu cầu sau:
- Giá trị và tham số phải là literal type và bản thân nó phải là const.
- Không được là virtual functions
- Không được chứa try-block và go to
- Các biến và giá trị trong thân hàm phải tự động được khi hết thúc hàm
Cụ thể hơn các bạn có đọc thêm tại đây
Kết luận
Như vậy trong bài này mình đã giới thiệu cho các bạn một phương pháp hay rất hay để áp dụng compile-time cho hàm giúp tăng hiệu năng của chương trình. Tuy nhiên chúng ta cần cần tuân thủ và cẩn thận với việc sử dụng nó nhé ❤
