Templat meta pemrograman

38

Dapatkah seseorang menjelaskan kepada saya, mengapa cara meta-pemrograman templat pertama akan infinite loop, tetapi yang kedua berjalan dengan benar.

#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;
}
Exxul
sumber
2
Tujuannya adalah untuk menggunakan pemrograman meta template. constexprbukan pilihan.
Exxul
Menambahkan tag c ++ 98 untuk membuat eksplisit yang constexprbukan pilihan. (Ini diperkenalkan di C ++ 11). Itu tidak valid jawaban yang ada. Exxul, tolong jelaskan versi C ++ mana yang Anda terbatas.
MSalters
Maaf saya menghapus tag.
Exxul

Jawaban:

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

Baris ini menyebabkan instantiasi keduanya commondivs<N,(M-N)>::valdan commondivs<(N-M),M>::val, bahkan jika kondisi diketahui pada waktu kompilasi dan salah satu cabang tidak akan pernah diambil.

Ganti ? :dengan std::conditional_t, yang tidak memiliki batasan ini:

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

Masalahnya adalah semua operan dari operator kondisional akan dievaluasi, sehingga keduanya commondivs<N,(M-N)>dan commondivs<(N-M),M>akan dipakai dan mereka valdievaluasi dan kemudian mengarah ke instantiasi templat rekursif.

Anda dapat menerapkan constexpr jika dan memasukkannya ke dalam constexpr staticfungsi anggota.

Jika nilainya true, maka pernyataan-palsu dibuang (jika ada), jika tidak, pernyataan-benar dibuang.

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

HIDUP

songyuanyao
sumber
Dievaluasi atau hanya dipakai?
Daniel McLaury
@DanielMcLaury Dievaluasi; tidak hanya dipakai.
songyuanyao
Nilai ::valharus dihasilkan di kedua cabang yakin, tetapi ini masih instantiation (dari template dengan anggota const statis). Evaluasi pada waktu berjalan tidak terjadi ... yah, jelas tidak bisa karena tidak pernah dikompilasi ...
berguna
8

Operator ternary tidak seperti if constexpr: ketika kompilator melihatnya, ia harus membuat kode untuk kedua cabang. Dengan kata lain, untuk instantiate template commondivs<M, N>, kompiler instantiates baik template commondivs<N, M - N>dan commondivs<N - M, M>.

Berbeda dengan itu, commondiv(N, M - N)dan commondiv(N - M, M)diterjemahkan ke dalam dua panggilan fungsi. Yang mana yang diambil, akan diputuskan ketika fungsi tersebut benar-benar dipanggil.

Tambahan.

HolyBlackCat memberikan solusi std::conditional_t. Ini satu lagi:

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;
};
Evg
sumber
0

Anda mendapatkan rekursi tak terbatas karena:

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

sama sekali bukan pemrograman metatemplate karena ?:, seperti yang dikatakan @Eng, tidak constexpr.

Anda ingin melihat jawaban @ HolyBlackCat.

Paul Evans
sumber
1
Itu tidak akan membantu. ?:tidak constexpr.
Evg
Tidak, saya mencobanya. Loop tak terbatas yang sama.
Exxul