Hợp pháp để khởi tạo một mảng trong hàm tạo constexpr?


11

Mã sau đây có hợp pháp không?

template <int N>
class foo {
public:
    constexpr foo()
    {
        for (int i = 0; i < N; ++i) {
            v_[i] = i;
        }
    }

private:
    int v_[N];
};

constexpr foo<5> bar;

Clang chấp nhận nó, nhưng GCC và MSVC từ chối nó.

Lỗi của GCC là:

main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
   15 | constexpr foo<5> bar;
      |                  ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
    4 |     constexpr foo()
      |               ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
   12 |     int v_[N];
      |         ^~

Nếu loại mã này ổn, tôi có thể cắt giảm một vài cách sử dụng index_sequences.


1
Gcc10 cũng chấp nhận nó.
songyuanyao

bạn có thể ghi lại lỗi từ MSVC không?
max66

... và GCC cũng vậy.
Evg

1
@songyuanyao - g ++ 10 chấp nhận nó biên dịch C ++ 20; từ chối nó biên dịch C ++ 17 trở lên; điểm có vẻ như _vnên được khởi tạo trong danh sách khởi tạo, cho đến C ++ 17. Có lẽ đã thay đổi một cái gì đó trong C ++ 20.
max66

2
@Evg Điều đó thực sự thú vị, bởi vì nó có thể gợi ý Clang sử dụng "nhận thức" của mình rằng một đối tượng có thời lượng lưu trữ tĩnh bị bỏ qua để nói "không sao, đối tượng này có thể đã được khởi tạo mặc định nhưng đọc từ intthành viên của nó sẽ không bao giờ có hành vi không xác định ". Tôi tự hỏi liệu GCC không làm điều đó có tuân thủ hay không, hay ngược lại ...
Cuộc đua nhẹ nhàng trong quỹ đạo

Câu trả lời:


13

Khởi tạo mặc định tầm thường đã bị cấm trong constexprngữ cảnh cho đến C ++ 20 .

Lý do, tôi đoán, là rất dễ "vô tình" đọc từ các nguyên thủy khởi tạo mặc định, một hành động mang lại cho chương trình của bạn hành vi không xác định và các biểu thức có hành vi không xác định bị cấm ngay lập tức bị cấm constexpr( ref ). Ngôn ngữ đã được mở rộng để bây giờ trình biên dịch phải kiểm tra xem việc đọc như vậy có xảy ra hay không và nếu không, việc khởi tạo mặc định sẽ được chấp nhận. Đó là một công việc nhiều hơn cho trình biên dịch, nhưng (như bạn đã thấy!) Có lợi ích đáng kể cho lập trình viên.

Bài viết này đề xuất cho phép khởi tạo mặc định cho các kiểu xây dựng mặc định tầm thường trong bối cảnh constexpr trong khi tiếp tục không cho phép gọi hành vi không xác định. Nói tóm lại, miễn là các giá trị chưa được khởi tạo không được đọc từ đó, các trạng thái như vậy phải được cho phép trong constexpr trong cả hai kịch bản heap và stack được phân bổ.

Kể từ C ++ 20, việc bỏ mặc v_"chưa được cấp phép" như bạn có là hợp pháp. Sau đó, bạn tiếp tục gán tất cả các giá trị phần tử của nó, điều này thật tuyệt.


4
@ max66 Tôi cũng vậy! Tất cả những gì tôi đã làm là quét danh sách thay đổi C ++ 20 trên Wikipedia, tìm thứ gì đó có liên quan constexprvà đọc lướt đề xuất được liên kết;)
Các cuộc đua Lightness trong Orbit

3
Điều tồi tệ là hơn 20 năm tôi đang sử dụng C ++. Nếu mỗi ngày tôi học được điều gì đó mới ... hoặc tôi là một lập trình viên tồi hoặc C ++ trở nên quá phức tạp.
max66

5
@ max66 Nó gần như chắc chắn là cái sau. Ngoài ra, việc nó cứ thay đổi căn bản cứ sau vài năm lại khiến nó trở thành mục tiêu di chuyển nhanh. Ai có thể theo kịp điều đó?! Ngay cả các trình biên dịch cũng không theo kịp điều đó.
Các cuộc đua nhẹ nhàng trong quỹ đạo

@ max66 Bài báo này xuất hiện trong đầu: Hãy nhớ đến Vasa!
Evg

@Evg Oh, wow, tờ giấy đó đã vượt qua tôi (IRONY). Tại chỗ trên!
Cuộc đua nhẹ nhàng trong quỹ đạo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.