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


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;

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


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

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.


1
Itu tidak akan membantu. ?:tidak constexpr.
Evg

Tidak, saya mencobanya. Loop tak terbatas yang sama.
Exxul
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.