nếu constexpr với static_assert trong lambda, trình biên dịch nào là đúng?


13

Khi chúng ta muốn sử dụng một static_asserttrong if constexprchúng ta phải làm cho tình trạng phụ thuộc vào một số mẫu tham số. Thật thú vị, gcc và clang không đồng ý khi mã được bọc trong lambda.

Đoạn mã sau biên dịch với gcc, nhưng tiếng kêu kích hoạt xác nhận, ngay cả khi điều đó if constexprkhông đúng.

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

Sống ví dụ ở đây .

Nó có thể dễ dàng được sửa chữa bằng cách thay thế False<T>bằng False<decltype(x)>.

Vậy câu hỏi là: trình biên dịch nào đúng? Tôi cho rằng gcc là chính xác vì điều kiện trong static_assertphụ thuộc vào T, nhưng tôi không chắc chắn.


Điều này hỏi loại câu hỏi tương tự nhưng đến từ hướng ngược lại: stackoverflow.com/questions/59393908/ Ấn .
NathanOliver

1
@mfnx Không thể sao chép . Bạn có thể chia sẻ một ví dụ?
NathanOliver

2
Tôi muốn nói cả hai đều đúng (NDR hình thành): static_assert(False<int>, "AAA");tương đương với static_assert(false, "AAA");bên trong lambda.
Jarod42

2
@mfnx Bạn đã thay đổi giá trị của hằng. Sử dụng ví dụ của OP trong đó hằng số là f(std::integral_constant<int, 1>{});Wandbox không kích hoạt xác nhận: Wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver Có bạn đúng, xin lỗi vì tiếng ồn. Có vẻ như gcc chỉ đúng khi mã đó trong constexpr nên bị loại bỏ nếu hằng số> = 0;
mfnx

Câu trả lời:


1

Từ [stmt.if] / 2 (nhấn mạnh của tôi)

Nếu câu lệnh if có dạng if constexpr, giá trị của điều kiện sẽ là biểu thức hằng số được chuyển đổi theo ngữ cảnh của kiểu bool; hình thức này được gọi là constexpr if statement. Nếu giá trị của điều kiện được chuyển đổi là sai, thì phần thứ nhất là một câu lệnh bị loại bỏ, nếu không thì phần thứ hai, nếu có, là một câu lệnh bị loại bỏ. Trong quá trình khởi tạo của một thực thể templated kèm theo ([temp.pre]), nếu điều kiện không phụ thuộc vào giá trị sau khi khởi tạo, thì phần phụ bị loại bỏ (nếu có) sẽ không được khởi tạo.

Đọc rằng người ta sẽ nghĩ rằng khẳng định tĩnh sẽ bị loại bỏ, nhưng đây không phải là trường hợp.

Xác nhận tĩnh được kích hoạt trong giai đoạn đầu tiên của mẫu vì trình biên dịch biết nó luôn luôn sai.

Từ [temp.res] / 8 (nhấn mạnh của tôi)

Tính hợp lệ của một mẫu có thể được kiểm tra trước khi khởi tạo. [ Lưu ý: Biết tên nào là tên loại cho phép kiểm tra cú pháp của mọi mẫu theo cách này. - lưu ý cuối ] Chương trình không đúng định dạng, không cần chẩn đoán, nếu:

  • (8.1) không có chuyên môn hợp lệ nào có thể được tạo cho một mẫu hoặc thay thế một constexpr nếu câu lệnh trong một mẫu và mẫu không được khởi tạo , hoặc

[...]

Vâng thực sự, bạn False<T>phụ thuộc vào T. Vấn đề là lambda chung là một mẫu và False<T>không phụ thuộc vào bất kỳ tham số mẫu nào của lambda.

Đối với một Tđiều đó False<T>là sai, khẳng định tĩnh sẽ luôn luôn là sai, bất kể đối số khuôn mẫu nào được gửi đến lambda.

Trình biên dịch có thể thấy rằng đối với bất kỳ khởi tạo nào của mẫu operator(), xác nhận tĩnh sẽ luôn kích hoạt cho T. hiện tại do đó lỗi trình biên dịch.

Một giải pháp cho điều này sẽ phụ thuộc vào x:

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

Ví dụ sống


13

Quy tắc thông thường ở đây là [temp.res] / 8 :

Chương trình không được định dạng đúng, không cần chẩn đoán, nếu: không thể tạo chuyên môn hợp lệ cho mẫu hoặc thay thế một constexpr nếu câu lệnh trong mẫu và mẫu không được khởi tạo

Một khi bạn khởi tạo foo<T>, static_assertbạn không còn phụ thuộc nữa. Nó trở thành static_assert(false)- cho tất cả các cảnh báo có thể có của toán tử cuộc gọi của lambda chung f. Đó là hình thành xấu, không cần chẩn đoán. Chẩn đoán Clang, gcc không. Cả hai đều đúng.

Lưu ý rằng nó không quan trọng rằng static_assertđây loại bỏ.

Nó có thể dễ dàng được sửa chữa bằng cách thay thế False<T>bằng False<decltype(x)>.

Điều này giữ cho sự static_assertphụ thuộc trong lambda chung, và bây giờ chúng ta rơi vào trạng thái có thể giả định là một chuyên môn hợp lệ, vì vậy chúng ta không còn bị coi thường nữa, ndr.

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.