การเขียนโปรแกรมเมตาเทมเพลต


38

มีใครบางคนสามารถอธิบายให้ฉันฟังได้ว่าทำไมเมธอดการเขียนโปรแกรมเมตาเทมเพลตแรกกำลังจะวนซ้ำไม่สิ้นสุด แต่อันที่สองทำงานได้อย่างถูกต้อง

#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
เป้าหมายคือใช้การเขียนโปรแกรมเมตาเทมเพลต constexprไม่ใช่ตัวเลือก
Exxul

เพิ่มแท็ก c ++ 98 เพื่อให้ชัดเจนว่าconstexprไม่ใช่ตัวเลือก (มันถูกนำมาใช้ใน C ++ 11) นั่นไม่ถูกต้องคำตอบที่มีอยู่ Exxul โปรดอธิบายว่าคุณต้อง จำกัด รุ่น C ++ เพียงใด
MSalters

ขออภัยฉันลบแท็ก
Exxul

คำตอบ:


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

บรรทัดนี้ทำให้เกิดอินสแตนซ์ของทั้งคู่commondivs<N,(M-N)>::valและcommondivs<(N-M),M>::valแม้ว่าจะรู้เงื่อนไขในเวลารวบรวมและสาขาใดสาขาหนึ่งจะไม่ถูกนำมาใช้

แทนที่? :ด้วยstd::conditional_tซึ่งไม่มีข้อ จำกัด นี้:

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

15

ปัญหาคือตัวถูกดำเนินการทั้งหมดของโอเปอเรเตอร์ที่มีเงื่อนไขจะได้รับการประเมินดังนั้นทั้งcommondivs<N,(M-N)>และcommondivs<(N-M),M>รับอินสแตนซ์และvalรับการประเมินจากนั้นจึงนำไปสู่อินสแตนซ์ของแม่แบบเรียกซ้ำ

คุณสามารถใช้constexpr ถ้าและวางไว้ในconstexpr staticฟังก์ชั่นสมาชิก

หากค่าtrueนั้นแสดงว่า statement-false ถูกยกเลิก (ถ้ามี) มิฉะนั้น statement-true จะถูกยกเลิก

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();
};

มีชีวิต


ประเมินหรือยกตัวอย่าง?
Daniel McLaury

@DanielMcLaury ประเมินแล้ว; ไม่เพียงยกตัวอย่าง
songyuanyao

::valต้องสร้างค่าของทั้งสองสาขา แต่ยังคงเป็นอินสแตนซ์ (ของแม่แบบที่มีสมาชิก const แบบคงที่) การประเมินผล ณ รันไทม์ไม่ได้เกิดขึ้น ... มันชัดเจนว่าทำไม่ได้เพราะไม่เคยรวบรวม ...
ไร้ประโยชน์

8

ผู้ประกอบการที่ไม่เหมือนใครif constexpr: เมื่อคอมไพเลอร์เห็นมันก็จะต้องสร้างรหัสสำหรับสาขาทั้งสอง ในคำอื่น ๆ ที่จะยกตัวอย่างแม่แบบcommondivs<M, N>เป็นคอมไพเลอร์ instantiates ทั้งแม่และcommondivs<N, M - N>commondivs<N - M, M>

ตรงกันข้ามกับสิ่งนั้นcommondiv(N, M - N)และcommondiv(N - M, M)ถูกแปลเป็นการเรียกฟังก์ชันสองอย่าง จะเลือกอันไหนจะได้รับการตัดสินเมื่อมีการเรียกใช้ฟังก์ชันจริง

ส่วนที่เพิ่มเข้าไป.

HolyBlackCatstd::conditional_tให้โซลูชั่นที่มี นี่คืออีกหนึ่ง:

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

คุณได้รับการเรียกซ้ำแบบไม่สิ้นสุดเนื่องจาก:

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

ไม่ได้ metatemplate การเขียนโปรแกรมที่ทุกคนเพราะ?:เป็น @Eng constexprกล่าวว่าไม่ได้เป็น

คุณต้องการดูคำตอบของ @ HolyBlackCat


1
มันจะไม่ช่วย ไม่ใช่?: constexpr
Evg

ไม่ฉันลองแล้ว วงวนไม่สิ้นสุดเดียวกัน
Exxul
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.