C ++ 17 giới thiệu các biến nội tuyến
C ++ 17 khắc phục vấn đề này cho constexpr static
các biến thành viên yêu cầu định nghĩa ngoài dòng nếu nó được sử dụng odr. Xem nửa sau của câu trả lời này để biết chi tiết trước C ++ 17.
Đề xuất biến số nội tuyến P0386 giới thiệu khả năng áp dụng công inline
cụ xác định cho các biến. Đặc biệt trong trường hợp này constexpr
ngụ ý inline
cho các biến thành viên tĩnh. Đề xuất nói:
Trình xác định nội tuyến có thể được áp dụng cho các biến cũng như các hàm. Một biến được khai báo nội tuyến có cùng ngữ nghĩa với một hàm được khai báo nội tuyến: nó có thể được định nghĩa, theo định nghĩa, trong nhiều đơn vị dịch, phải được định nghĩa trong mọi đơn vị dịch trong đó sử dụng odr và hành vi của chương trình như thể có chính xác một biến.
và sửa đổi [basic.def] p2:
Một tuyên bố là một định nghĩa trừ khi
...
- nó khai báo một thành viên dữ liệu tĩnh bên ngoài một định nghĩa lớp và biến được định nghĩa trong lớp với bộ xác định constexpr (cách sử dụng này không được dùng nữa; xem [depr.static_constexpr]),
...
và thêm [depr.static_constexpr] :
Để tương thích với các tiêu chuẩn quốc tế C ++ trước đó, một thành viên dữ liệu tĩnh constexpr có thể được phân phối lại một cách dự phòng bên ngoài lớp mà không có trình khởi tạo. Việc sử dụng này không được chấp nhận. [ Thí dụ:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- ví dụ cuối]
C ++ 14 trở về trước
Trong C ++ 03, chúng tôi chỉ được phép cung cấp các trình khởi tạo trong lớp cho các kiểu tích phân const hoặc kiểu liệt kê const , trong C ++ 11 sử dụng constexpr
điều này đã được mở rộng thành các kiểu chữ .
Trong C ++ 11, chúng tôi không cần cung cấp định nghĩa phạm vi không gian tên cho constexpr
thành viên tĩnh nếu nó không được sử dụng , chúng ta có thể thấy điều này từ phần dự thảo tiêu chuẩn C ++ 11 9.4.2
[class.static.data] nói ( nhấn mạnh của tôi đi về phía trước ):
[...] Một thành viên dữ liệu tĩnh có kiểu chữ có thể được khai báo trong định nghĩa lớp với trình xác định constexpr; nếu vậy, khai báo của nó sẽ chỉ định một bộ khởi tạo dấu ngoặc hoặc hoặc bằng nhau trong đó mọi mệnh đề khởi tạo là một biểu thức gán là một biểu thức không đổi. [Lưu ý: Trong cả hai trường hợp này, thành viên có thể xuất hiện trong các biểu thức không đổi. Lưu ý về mối quan hệ]
Thành viên vẫn sẽ được xác định trong phạm vi không gian tên nếu nó được sử dụng odr (3.2) trong chương trình và định nghĩa phạm vi không gian tên sẽ không chứa bộ khởi tạo.
Vì vậy, câu hỏi trở thành, được baz
sử dụng ở đây:
std::string str(baz);
và câu trả lời là có , và vì vậy chúng tôi cũng yêu cầu một định nghĩa phạm vi không gian tên.
Vậy làm thế nào để chúng ta xác định nếu một biến được sử dụng odr ? Từ ngữ C ++ 11 ban đầu trong phần 3.2
[basic.def.odr] nói:
Một biểu thức có khả năng được đánh giá trừ khi nó là toán hạng không được đánh giá (Điều 5) hoặc biểu thức con của nó. Một biến có tên xuất hiện dưới dạng biểu thức được đánh giá tiềm năng được sử dụng odr trừ khi
nó là đối tượng thỏa mãn các yêu cầu xuất hiện trong biểu thức không đổi (5.19) và chuyển đổi lvalue sang rvalue (4.1) ngay lập tức được áp dụng .
Vì vậy, baz
không mang lại một biểu thức không đổi nhưng chuyển đổi lvalue-rvalue không được áp dụng ngay lập tức vì nó không được áp dụng do baz
là một mảng. Điều này được đề cập trong phần 4.1
[conv.lval] có nội dung:
Một giá trị (3.10) của loại T không có chức năng, không có mảng có thể được chuyển đổi thành giá trị.53 [...]
Những gì được áp dụng trong chuyển đổi mảng thành con trỏ .
Từ ngữ này của [basic.def.odr] đã bị thay đổi do Báo cáo khuyết tật 712 do một số trường hợp không được bao phủ bởi từ ngữ này nhưng những thay đổi này không thay đổi kết quả cho trường hợp này.