Lập trình meta mẫu


38

Ai đó có thể giải thích cho tôi, tại sao cách lập trình meta mẫu đầu tiên sẽ đi vào vòng lặp vô hạn, nhưng cách thứ hai chạy chính xác.

#include <iostream>
using namespace std;

template<int N, int M>
struct commondivs {                                              
  static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;
};

template<int N>
struct commondivs<N,N> {
  static const int val = N;
};


int commondiv(int N, int M){
    if(N==M){
        return N;
    }   
    return (N<M)?commondiv(N,(M-N)):commondiv((N-M),M);     
}

int main() {

    cout << commondivs<9,6>::val << endl;
    cout << commondiv(9,6) << endl;
    return 0;
}


2
Mục tiêu là sử dụng lập trình meta mẫu. constexprkhông phải là một sự lựa chọn.
Exxul

Đã thêm thẻ c ++ 98 để làm rõ ràng constexprkhông phải là một tùy chọn. (Nó được giới thiệu trong C ++ 11). Điều đó không làm mất hiệu lực câu trả lời hiện có. Exxul, vui lòng làm rõ phiên bản C ++ nào bạn giới hạn.
MSalters

Xin lỗi tôi đã xóa thẻ.
Exxul

Câu trả lời:


44
(N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val

Dòng này gây ra sự khởi tạo của cả hai commondivs<N,(M-N)>::valcommondivs<(N-M),M>::val, ngay cả khi điều kiện được biết tại thời điểm biên dịch và một trong các nhánh sẽ không bao giờ được thực hiện.

Thay thế ? :bằng std::conditional_t, không có giới hạn này:

static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;

15

Vấn đề là tất cả các toán hạng của toán tử điều kiện sẽ được đánh giá, vì vậy cả hai commondivs<N,(M-N)>commondivs<(N-M),M>được khởi tạo và chúng valđược đánh giá và sau đó dẫn đến khởi tạo mẫu đệ quy.

Bạn có thể áp dụng constexpr nếu và đặt nó trong constexpr statichàm thành viên.

Nếu giá trị là true, thì statement-false bị loại bỏ (nếu có), nếu không, statement-true sẽ bị loại bỏ.

template<int N, int M>
struct commondivs {                                              
  constexpr static int get_val() {
    if constexpr (N<M) return commondivs<N,(M-N)>::val; // if true, the else part won't be evaluated
    else return commondivs<(N-M),M>::val;               // vice versa
  }
  static const int val = get_val();
};

TRỰC TIẾP


Đánh giá hay chỉ khởi tạo?
Daniel McLaury

@DanielMcLaury Đánh giá; không chỉ khởi tạo.
songyuanyao

Giá trị của ::valphải được tạo trên cả hai nhánh chắc chắn, nhưng đây vẫn là khởi tạo (của một mẫu có thành viên const tĩnh). Đánh giá trong thời gian chạy không xảy ra ... tốt, rõ ràng là không thể vì nó không bao giờ được biên dịch ...
Vô dụng

8

Toán tử ternary không giống như if constexpr: khi trình biên dịch nhìn thấy nó, nó phải tạo mã cho cả hai nhánh. Nói cách khác, để khởi tạo một mẫu commondivs<M, N>, trình biên dịch sẽ khởi tạo cả hai mẫu commondivs<N, M - N>commondivs<N - M, M>.

Ngược lại với điều đó, commondiv(N, M - N)commondiv(N - M, M)được dịch thành hai lệnh gọi hàm. Cái nào được lấy, sẽ được quyết định khi hàm thực sự được gọi.

Thêm vào.

HolyBlackCat đã đưa ra một giải pháp với std::conditional_t. Đây là một cái nữa:

template<int N, int M>
struct commondivs {                                              
    static constexpr int min = (N < M) ? N : M;
    static constexpr int max = (N < M) ? M : N;
    static constexpr int val = commondivs<min, max - min>::val;
};

template<int N>
struct commondivs<N, N> {
    static constexpr int val = N;
};

0

Bạn nhận được đệ quy vô hạn vì:

static const int val = (N<M) ? commondivs<N,(M-N)>::val : commondivs<(N-M),M>::val;

hoàn toàn không phải là lập trình siêu dữ liệu bởi vì ?:, như @Eng nói, là không constexpr.

Bạn muốn xem câu trả lời của @ HolyBlackCat.


1
Nó sẽ không giúp. ?:không phải là constexpr.
Evg

Không, tôi thử nó. Vòng lặp vô hạn giống nhau.
Exxul
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.