Meta programmazione dei template


38

Qualcuno può spiegarmi, perché il primo modo di meta-programmazione del modello sta andando a ciclo infinito, ma il secondo funziona correttamente.

#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
L'obiettivo era utilizzare la meta programmazione dei template. constexprnon è un'opzione.
Exxul

Aggiunto tag c ++ 98 per rendere esplicito che constexprnon è un'opzione. (È stato introdotto in C ++ 11). Ciò invalida le risposte esistenti. Exxul, chiarisci a quale versione C ++ sei limitato.
Sali il

Spiacenti, ho rimosso il tag.
Exxul,

Risposte:


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

Questa linea provoca l'istanza di entrambi commondivs<N,(M-N)>::vale commondivs<(N-M),M>::val, anche se la condizione è nota al momento della compilazione e uno dei rami non verrà mai preso.

Sostituisci ? :con std::conditional_t, che non presenta questa limitazione:

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

15

Il problema è che verranno valutati tutti gli operandi dell'operatore condizionale, quindi entrambi commondivs<N,(M-N)>e commondivs<(N-M),M>vengono istanziati e loro valvengono valutati e quindi porta a un'istanza del modello ricorsiva.

È possibile applicare constexpr if e inserirlo in una constexpr staticfunzione membro.

Se il valore è true, viene quindi scartato statement-false (se presente), altrimenti viene scartato 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();
};

VIVERE


Valutato o solo istanziato?
Daniel McLaury,

@DanielMcLaury valutato; non solo istanziato.
Songyuanyao,

Il valore di ::valdeve essere sicuramente generato su entrambi i rami, ma si tratta comunque di un'istanza (di un modello con un membro const statico). La valutazione in fase di esecuzione non avviene ... beh, ovviamente non può, poiché non viene mai compilata ...
Inutile

8

L'operatore ternario non è come if constexpr: quando un compilatore lo vede, deve generare codice per entrambi i rami. In altre parole, per creare un'istanza di un modello commondivs<M, N>, un compilatore crea un'istanza di entrambi i modelli commondivs<N, M - N>e commondivs<N - M, M>.

Al contrario, commondiv(N, M - N)e commondiv(N - M, M)sono tradotti in due chiamate di funzione. Quale verrà preso, verrà deciso quando viene effettivamente chiamata la funzione.

Addizione.

HolyBlackCat ha dato una soluzione con std::conditional_t. Eccone un altro:

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

Ottieni una ricorsione infinita perché:

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

non è affatto una programmazione metatemplata perché ?:, come dice @Eng, non lo è constexpr.

Vuoi dare un'occhiata alla risposta di @ HolyBlackCat.


1
Non aiuterà. ?:non lo è constexpr.
Evg

No, ci provo. Lo stesso ciclo infinito.
Exxul,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.