Gọi hàm thành viên `constexpr` thông qua tham chiếu - clang vs gcc


8

Hãy xem xét ví dụ sau ( đoạn trích (0) ):

struct X
{
    constexpr int get() const { return 0; }
};

void foo(const X& x)
{
    constexpr int i = x.get();
}

int main()
{
    foo(X{});
}

Ví dụ trên biên dịch với tất cả các phiên bản g++trước g++ 10.xvà không bao giờ được biên dịch theo clang++. Thông báo lỗi là:

error: 'x' is not a constant expression
    8 |     constexpr int i = x.get();
      |

ví dụ trực tiếp trên godbolt.org

Tuy nhiên, loại lỗi có ý nghĩa, xkhông bao giờ là một biểu thức không đổi trong cơ thể của foo:

  • X::get()được đánh dấu constexprvà nó không phụ thuộc vào trạng thái của x;

  • Thay đổi const X&để const Xlàm cho mã được biên dịch với mọi trình biên dịch (trên godbolt.org) đoạn trích (1) .


Nó thậm chí còn thú vị hơn khi tôi đánh dấu X::get()static( (trên godbolt.org) đoạn trích (2) ). Với sự thay đổi đó, tất cả các phiên bản đã thử nghiệm của g++(bao gồm cả thân cây) biên dịch, trong khi clang++vẫn luôn không biên dịch được.

Vì vậy, câu hỏi của tôi:

  • g++ 9.xđúng trong việc chấp nhận đoạn trích (0) không?

  • Có phải tất cả các trình biên dịch đều đúng trong việc chấp nhận đoạn trích (1) không? Nếu vậy, tại sao các tài liệu tham khảo có ý nghĩa?

  • g++ 9.xg++ trunkchính xác trong việc chấp nhận đoạn trích (2) ?


constexpr là một 'mặt nạ' cho một giá trị và const là một biến mà bạn thông báo nó sẽ không bị thay đổi. Các giá trị không thể được tham chiếu hoặc chỉ nhưng const. Thành viên hàm get () được xử lý trong thời gian biên dịch. Nói cách khác, nó là 0 cho tất cả các trường hợp. Như tôi đã nói trước đây, các giá trị không thể được tham chiếu hoặc trỏ
TheArquitect

3
Tôi nghĩ rằng [expr.const] /2.11 áp dụng ở đây, và xtrong fookhông phải là một biểu thức hằng. Thậm chí còn có một báo cáo lỗi cũ (bị từ chối không chính thức) về tiếng kêu cho hành vi đúng của nó (trong khi GCC có một lỗi thực sự cho nó).
dfri


3
Đã viết về điều này gần đây, có vẻ có liên quan: brevzin.github.io/c++/2020/02/05/constexpr-array-size
Barry

@Barry hay, trong bài viết của bạn, bạn cũng có tài liệu tham khảo báo cáo lỗi GCC tương ứng (tôi có thể triển khai tìm clang bị từ chối) cho phép gcc 9.x chấp nhận đoạn 0, đã được sửa.
dfri

Câu trả lời:


12

G ++ 9.x có đúng khi chấp nhận đoạn trích (0) không?

Không.

Có phải tất cả các trình biên dịch đều đúng trong việc chấp nhận đoạn trích (1) không? Nếu vậy, tại sao các tài liệu tham khảo có ý nghĩa?

Vâng, họ là.

Một biểu thức hằng không thể sử dụng một biểu thức id đặt tên cho một tham chiếu không có khởi tạo biểu thức hằng trước đó hoặc bắt đầu thời gian tồn tại của nó trong quá trình đánh giá biểu thức hằng. [expr.const] /2.11 ( tương tự trong C ++ 20 )

Điều này cũng không đúng nếu bạn đặt tên một biến không tham chiếu mà không liên quan đến bất kỳ chuyển đổi lvalue-to-rvalue nào. x.get()chỉ được gọi xlà lvalue và chỉ gọi một constexprchức năng không thực sự truy cập bất kỳ thành viên nào x, vì vậy không có vấn đề gì.

Các thân g ++ 9.x và g ++ có đúng khi chấp nhận đoạn trích (2) không?

Không, bởi vì biểu thức vẫn chứa biểu thức con xvi phạm quy tắc được đề cập ở trên.


Báo cáo lỗi có liên quan (bị từ chối không chính thức) về clang chỉ ra tính chính xác tương tự trong clang (luôn luôn) không biên dịch đoạn trích 0.
dfri
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.