2017-07-10 11 views
14

Sau đây thất bại trong việc biên dịch theo cả gcc và kêu vang trong C++ 14, nhưng thành công với C++ 1Z:Định nghĩa lại các thành viên dữ liệu tĩnh constexpr có được phép không? (nhưng không phải là nội tuyến const)?

struct Cls { 
    static constexpr int N = 0; 
}; 
constexpr int Cls::N; 
constexpr int Cls::N; 

Các lỗi C++ 14 là có thể dự đoán: redefinition of ‘constexpr const int Cls::N’

gì thay đổi làm cho điều này hợp pháp? Tôi tìm thấy:

n4659 10.1.5 [dcl.constexpr]

Một chức năng hoặc thành viên dữ liệu tĩnh tuyên bố với constexpr specifier là ngầm một hàm nội tuyến hoặc biến

Vì vậy, Tôi nghĩ rằng nó có thể phải làm với các biến nội tuyến, nhưng sau đây không cho C++ 1z theo cả hai trình biên dịch

struct Cls { 
    static inline const int N = 0; 
}; 
inline const int Cls::N; // note, only one definition here 

Trả lời

12

Trước C++ 17, bạn cần phải khai báo lại tất cả các biến số static bên ngoài lớp trong một đơn vị dịch thuật (thường là mỗi đơn vị dịch là .cpp và ngược lại, nhưng không bắt buộc). Như bạn đã chỉ ra, C++ 17 giới thiệu inline biến thành viên lớp học và các biến số static constexpr tự động đủ điều kiện. Bạn được không được phép redeclare inline biến bên ngoài lớp, như bạn đã thấy trong ví dụ thứ hai của mình, nhưng ngoại lệ được thực hiện cho constexpr vì trước đây bạn được phép (và thực tế bắt buộc) để làm như vậy, nhưng cú pháp không được chấp nhận.

Trong [class.static.data] p2, nó cho phép cú pháp cho thành viên không nội tuyến (“Khai báo thành viên dữ liệu tĩnh không nội tuyến trong định nghĩa lớp không phải là định nghĩa và có thể là chưa hoàn thành loại khác với cv void.Định nghĩa cho một thành viên dữ liệu tĩnh không được xác định nội tuyến trong định nghĩa lớp sẽ xuất hiện trong một phạm vi không gian tên kèm theo định nghĩa lớp của thành viên. ”)

Trong đoạn tiếp theo, tiêu chuẩn cho phép constexpr khai báo ngoài lớp và yêu cầu chúng không phải là constexpr dữ liệu (được thêm vào):

Nếu một non-volatile phi inline const thành viên dữ liệu tĩnh là không thể thiếu hoặc liệt kê loại, khai của nó trong định nghĩa lớp có thể chỉ định một cú đúp-hoặc-bằng-initializer trong mà mỗi initializer-khoản rằng là biểu thức chuyển nhượng là một biểu thức liên tục (8.20). 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 không đúng (6.2) trong chương trình và định nghĩa phạm vi không gian tên sẽ không chứa một bộ khởi tạo . Một thành viên dữ liệu tĩnh có thể được định nghĩa trong định nghĩa lớp và có thể chỉ định trình khởi tạo dấu ngoặc đơn hoặc bằng nhau. Nếu thành viên là được khai báo với thông số constexpr, nó có thể được redeclared trong phạm vi không gian tên không có bộ khởi tạo (việc sử dụng này không được chấp nhận; xem D.1).Tuyên bố của các thành viên dữ liệu tĩnh khác không được chỉ định bộ khởi tạo dấu ngoặc đơn hoặc bằng nhau.

Và đây là việc không dùng lưu ý, Q.1 khai báo lại các tĩnh thành viên dữ liệu constexpr [depr.static_constexpr]:

Đối với khả năng tương thích với tiêu chuẩn quốc tế trước khi C++, một constexpr thành viên dữ liệu tĩnh có thể được redeclared dư thừa bên ngoài lớp không có bộ khởi tạo. Việc sử dụng này không được chấp nhận. [Ví dụ:

struct A { 
    static constexpr int n = 5; // definition (declaration in C++ 2014) 
}; 
constexpr int A::n; // redundant declaration (definition in C++ 2014) 

- end dụ]

Các vấn đề liên quan