Có sự khác biệt giữa các định nghĩa sau đây?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Nếu không, phong cách nào được ưa thích trong C ++ 11?
Có sự khác biệt giữa các định nghĩa sau đây?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Nếu không, phong cách nào được ưa thích trong C ++ 11?
Câu trả lời:
Tôi tin rằng có một sự khác biệt. Hãy đổi tên chúng để chúng ta có thể nói về chúng dễ dàng hơn:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Cả hai PI1
và PI2
là hằng số, có nghĩa là bạn không thể sửa đổi chúng. Tuy nhiên chỉ PI2
là một hằng số thời gian biên dịch. Nó sẽ được khởi tạo tại thời gian biên dịch. PI1
có thể được khởi tạo tại thời gian biên dịch hoặc thời gian chạy. Hơn nữa, chỉ PI2
có thể được sử dụng trong ngữ cảnh yêu cầu hằng số thời gian biên dịch. Ví dụ:
constexpr double PI3 = PI1; // error
nhưng:
constexpr double PI3 = PI2; // ok
và:
static_assert(PI1 == 3.141592653589793, ""); // error
nhưng:
static_assert(PI2 == 3.141592653589793, ""); // ok
Bạn nên sử dụng loại nào? Sử dụng bất cứ điều gì đáp ứng nhu cầu của bạn. Bạn có muốn đảm bảo rằng bạn có hằng số thời gian biên dịch có thể được sử dụng trong các ngữ cảnh yêu cầu hằng số thời gian biên dịch không? Bạn có muốn khởi tạo nó với một tính toán được thực hiện trong thời gian chạy không? Vân vân.
const int N = 10; char a[N];
các công trình và giới hạn mảng phải là hằng số thời gian biên dịch.
PI1
thành hằng số tích phân thời gian biên dịch để sử dụng trong một mảng, nhưng không sử dụng như một tham số mẫu tích phân không loại. Vì vậy, khả năng chuyển đổi thời gian biên dịch PI1
thành một loại tích phân có vẻ hơi khó hiểu đối với tôi.
enum
khởi tạo là hai khác biệt đáng chú ý duy nhất giữa const
và constexpr
(và không hoạt động cho double
dù sao).
1 / PI1
và 1 / PI2
có thể mang lại kết quả khác nhau. Tôi không nghĩ rằng kỹ thuật này khá quan trọng như lời khuyên trong câu trả lời này.
constexpr double PI3 = PI1;
hoạt động chính xác cho tôi. (MSVS2013 CTP). Tôi đang làm gì sai?
Không có sự khác biệt ở đây, nhưng nó quan trọng khi bạn có một loại có hàm tạo.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
là một hằng số, nhưng nó không hứa sẽ được khởi tạo tại thời gian biên dịch. s1
được đánh dấu constexpr
, vì vậy nó là một hằng số và, bởi vì hàm tạo của nó S
cũng được đánh dấu constexpr
, nó sẽ được khởi tạo tại thời gian biên dịch.
Chủ yếu là vấn đề này khi khởi tạo trong thời gian chạy sẽ tốn thời gian và bạn muốn đẩy công việc đó lên trình biên dịch, nơi nó cũng tốn thời gian, nhưng không làm chậm thời gian thực hiện của chương trình được biên dịch
constexpr
sẽ dẫn đến chẩn đoán nếu việc tính toán thời gian biên dịch của đối tượng là không thể. Điều ít rõ ràng hơn là liệu một hàm mong đợi một tham số không đổi có thể được thực thi tại thời gian biên dịch hay không nếu tham số được khai báo là const
và không phải là constexpr
: tức là, sẽ constexpr int foo(S)
được thực thi tại thời gian biên dịch nếu tôi gọi foo(s0)
?
foo(s0)
sẽ được thực thi tại thời gian biên dịch, nhưng bạn không bao giờ biết: trình biên dịch được phép thực hiện tối ưu hóa như vậy. Chắc chắn, không gcc 4.7.2 cũng không kêu vang 3.2 cho phép tôi để biên dịchconstexpr a = foo(s0);
constexpr chỉ ra một giá trị không đổi và được biết đến trong quá trình biên dịch.
const chỉ ra một giá trị không đổi; nó không bắt buộc phải biết trong quá trình biên dịch.
int sz;
constexpr auto arraySize1 = sz; // error! sz's value unknown at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Lưu ý rằng const không cung cấp bảo đảm giống như constexpr, vì các đối tượng const không cần phải được khởi tạo với các giá trị được biết trong quá trình biên dịch.
int sz;
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation
Tất cả các đối tượng constexpr là const, nhưng không phải tất cả các đối tượng const là constexpr.
Nếu bạn muốn trình biên dịch đảm bảo rằng một biến có một giá trị có thể được sử dụng trong các ngữ cảnh yêu cầu các hằng số thời gian biên dịch, công cụ để tiếp cận là constexpr chứ không phải const.
Một constexpr liên tục mang tính biểu tượng phải nhận được một giá trị được biết đến tại thời gian biên dịch. Ví dụ:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
constexpr int c2 = n+7; // Error: we don’t know the value of c2
// ...
}
Để xử lý trường hợp giá trị của một “biến” được khởi tạo với một giá trị không được biết đến tại thời gian biên dịch nhưng không bao giờ thay đổi sau khi khởi tạo, C ++ cung cấp một hình thức thứ hai của hằng số (một const ). Ví dụ:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
const int c2 = n+7; // OK, but don’t try to change the value of c2
// ...
c2 = 7; // error: c2 is a const
}
Các biến const const như vậy rất phổ biến vì hai lý do:
Tham khảo: "Lập trình: Nguyên tắc và thực hành sử dụng C ++" của Stroustrup