Nội dung bài viết
Nguyễn Hữu Phương Const và constexpr là một khái niệm cũ và cũng là khái niệm mới đối với một số lập trình viên, trong bài viết này tôi sẽ làm rõ hai khái niệm này để có thể giúp ích cho các bạn trong công việc của mình.

Giới thiệu

Trong lịch sử phát triển của C++, những lập trình viên hỗ trợ xây dựng lên C++ ngày càng hoàn thiện hơn, cải tiến tốc độ biên dịch là yếu tố rất được quan tâm đến. Từ đó qua các thế hệ C++ tiêu biểu như C++98, C++11, C++14 và tiếp đến là C++17, xuất hiện nhiều hơn các từ khóa mới, khái niệm mới. Trong bài viết này, tôi sẽ giải thích const và constexpr - một khái niệm được đưa ra từ C++11.

Tiền đề bài viết

Khi sử dụng C++ để lập trình, tôi thấy rằng một số từ khóa được đưa ra trong các chuẩn C++ mới có thể giúp ích khá nhiều trong giải quyết bài toán với hiệu suất khá tốt. Vậy nên tôi sẽ giải thích sự khác nhau giữa một khái niệm cũ và một khái niệm mới để bạn đọc có thể dễ dàng sử dụng.

Đối tượng hướng đến

Bài viết hướng đến lập trình viên đã có vốn kiến thức và có thời gian làm việc với C++. Muốn cải thiện hiệu suất trong giải quyết một số vấn đề hoặc đơn giản để sử dụng thêm nhiều tính năng hữu ích trong C++.

Mở đầu

Đối với khái niệm const, trong bài viết này tôi sẽ không đi quá sâu để phân tích, đây là một khái niệm khá quen thuộc trong lập trình C++, bạn có thể tìm hiểu một số vào viết Thao Tác Với constant Với C++. Tôi sẽ làm rõ khái niệm constexpr và so sánh với const.

Nhắc lại về const

Tôi sẽ tổng hợp một số điểm quan trọng trong bài viết trên để tiện hơn cho bạn:

const (Constant – Hằng) là từ khóa để chỉ định một biến hay đối tượng nào đó là hằng, tức là không thay đổi được giá trị, mọi hành động thay đổi giá trị của hằng đều được compiler báo lỗi.

const được dùng trong các trường hợp đối tượng cần định là biến thông thường, biến tham chiếu và tham số của hàm.

int main()
{
	// Biến thông thường.
	const int x = 7;

	// Biến tham chiếu
	int y = 8;
	const int &A = y;

	// Biến con trỏ
	int z = 9;
	int *const p = &x;

	return 0;
}

// Tham số của hàm
void printName(const char * ID)
{
	// phần xử lý.
}

Bạn có thể tìm hiểu cách sử dụng kỹ hơn trong bài viết trên. Nhưng từ các cách sử dụng trên, ta rút ra được ý nghĩa của const, mà tôi muốn bạn ghi nhớ:

const: Không được thay đổi giá trị.

Định nghĩa tổng quát về constexpr

constexpr là từ khóa có chức năng yêu cầu trình biên dịch tính toán hàm hoặc biến tại thời điểm biên dịch. Các biến và hàm này được sử dụng sau đó nếu được sử dụng với một số tham số phù hợp.

constexpr: thực thi tính toán tại thời điểm biên dịch.

constexpr variable

Khi khai báo một biến dạng constexpr thì so với const, nó cũng được hiểu là không thể thay đổi giá trị, nhưng khác là trong thời gian biên dịch, còn const sẽ thuộc thời gian thực thi. Tuy nhiên muốn sử dụng constexpr cần phải tuân theo những quy định sau:

  • Kiểu của biến được thể hiện phải là LiteralType.
  • Nó cần được khởi tạo ngay tại thời điểm định nghĩa biến.
constexpr int a = 2;  

constexpr function

Khi khai báo 1 hàm là constexpr function, hàm này phải thỏa các điều kiện:

  • Nó không phải là virtual function.
  • Kiểu trả về của hàm phải là LiteralType.
  • Các biến tham số của hàm phải là LiteralType.
constexpr int add(int a, int b)
{
	return a + b;
}

constexpr và thời gian biên dịch

Điều quan trọng nhất của constexpr là biểu thức được tính tại thời gian biên dịch, ta sẽ xét ví dụ sau để làm rõ điều này:

int add_vectors_size(const std::vector<int> & a, const std::vector<int> & b)  
{
	return a.size() + b.size();
}

Như đã làm với hàm add phía trên, ta chuyển hàm add_vectors_size về dạng constexpr:

constexpr int add_vectors_size(const std::vector<int> & a, const std::vector<int> & b)  
{
	return a.size() + b.size();
}

Tuy nhiên, cách làm này là không chính xác, vì tại thời điểm biên dịch, ta có tham số truyền vào là hai vector, tuy nhiên ta lại không biết kích thước của vector đó, vậy nên hàm này không thể thực hiện được.

Đối với một ví dụ khác:

template <std::size_t N1, std::size_t N2>
int add_arrays_size(const std::array<int, N1> & a,
                    const std::array<int, N2> & b)
{
	return a.size() + b.size();
}

Hàm này lại có thể hoàn toàn thực hiện được, do tại thời điểm biên dịch, ta hoàn toàn biết size của hai mảng a và b.

Tổng kết

Tuy rằng hai từ khóa này khá giống nhau, nhưng đã thấy được rằng chúng có ý nghĩa hoàn toàn khác biệt, từ bài viết hi vọng bạn có thể hiểu được mục đích tạo nên từ khóa constexpr.

THẢO LUẬN
ĐÓNG