nếu constexpr - tại sao câu lệnh bị loại bỏ hoàn toàn được kiểm tra?


14

Tôi đã loay hoay với c ++ 20 consteval trong GCC 10 và đã viết mã này

#include <optional>
#include <tuple>
#include <iostream>

template <std::size_t N, typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if_impl(Predicate&& pred,
                                                  Tuple&& t) noexcept {
  constexpr std::size_t I = std::tuple_size_v<std::decay_t<decltype(t)>> - N;

  if constexpr (N == 0u) {
    return std::nullopt;
  } else {
    return pred(std::get<I>(t))
               ? std::make_optional(I)
               : find_if_impl<N - 1u>(std::forward<decltype(pred)>(pred),
                                      std::forward<decltype(t)>(t));
  }
}

template <typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if(Predicate&& pred,
                                             Tuple&& t) noexcept {
  return find_if_impl<std::tuple_size_v<std::decay_t<decltype(t)>>>(
      std::forward<decltype(pred)>(pred), std::forward<decltype(t)>(t));
}

constexpr auto is_integral = [](auto&& x) noexcept {
    return std::is_integral_v<std::decay_t<decltype(x)>>;
};


int main() {
    auto t0 = std::make_tuple(9, 1.f, 2.f);
    constexpr auto i = find_if(is_integral, t0);
    if constexpr(i.has_value()) {
        std::cout << std::get<i.value()>(t0) << std::endl;
    }
}

Được cho là hoạt động giống như thuật toán tìm STL nhưng trên các bộ dữ liệu và thay vì trả về một trình vòng lặp, nó trả về một chỉ mục tùy chọn dựa trên một vị từ thời gian biên dịch. Bây giờ mã này biên dịch tốt và nó in ra

9

Nhưng nếu bộ dữ liệu không chứa một phần tử là một kiểu tách rời, chương trình sẽ không biên dịch, bởi vì i.value () vẫn được gọi trong một tùy chọn trống. Bây giờ tại sao vậy?



@AndyG không sửa nó, phải không? x)
Yamahari

Câu trả lời:


11

Đây chỉ là cách constexpr nếu hoạt động. Nếu chúng tôi kiểm tra [stmt.if] / 2

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. [...]

nhấn mạnh của tôi

Vì vậy, chúng ta có thể thấy rằng chúng ta chỉ không đánh giá biểu thức bị loại bỏ nếu chúng ta ở trong một mẫu và nếu điều kiện phụ thuộc vào giá trị. mainkhông phải là một mẫu hàm nên phần thân của câu lệnh if vẫn được trình biên dịch kiểm tra tính chính xác.

Cppreference cũng nói điều này trong phần của họ về constexpr nếu với:

Nếu một câu lệnh constexpr if xuất hiện bên trong một thực thể templated và nếu điều kiện không phụ thuộc vào giá trị sau khi khởi tạo, thì câu lệnh bị loại bỏ sẽ không được khởi tạo khi mẫu kèm theo được khởi tạo.

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
    // ... handle p
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // never instantiated with an empty argument list.
}

Bên ngoài một mẫu, một tuyên bố bị loại bỏ được kiểm tra đầy đủ. nếu constexpr không thay thế cho lệnh #if tiền xử lý:

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // Error even though in discarded statement
    }
}

Bạn có biết lý do cho việc này? có vẻ như điều này sẽ phù hợp nếu constexpr. Ngoài ra, giải pháp sẽ là ví dụ như gói nó trong một mẫu nào đó?
Yamahari

@Yamahari Bởi vì các mẫu C ++ đều có cấu trúc nhiều hơn và ít hơn bạn muốn. Và vâng, bọc nó trong một mẫu (hoặc viết như thế i.value_or(0))
Barry

2
@Yamahari Vâng, giải pháp sẽ là đặt mã trong một mẫu hàm. Theo như lý luận, tôi không biết tại sao. Đó có lẽ sẽ là một câu hỏi hay để hỏi.
NathanOliver

@Barry value_or (0) hoạt động tốt nhưng đối với trường hợp khi bộ dữ liệu có kích thước 0
Yamahari

@Yamahari Vâng ... không phải là một gợi ý tốt về phía tôi.
Barry
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.