Programowanie meta szablonów

38

Czy ktoś może mi wyjaśnić, dlaczego pierwszy sposób metaprogramowania szablonów prowadzi do nieskończonej pętli, ale drugi działa poprawnie.

#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
źródło
2
Celem było użycie szablonu meta programowania. constexprnie jest opcją.
Exxul
Dodano znacznik c ++ 98, aby wyraźnie zaznaczyć, że constexprnie jest to opcja. (Został wprowadzony w C ++ 11). To unieważnia istniejące odpowiedzi. Exxul, proszę wyjaśnić, do której wersji C ++ jesteś ograniczony.
MSalters
Przepraszam, usunąłem tag.
Exxul

Odpowiedzi:

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

Ta linia powoduje utworzenie wystąpienia obu commondivs<N,(M-N)>::vali commondivs<(N-M),M>::valnawet jeśli warunek jest znany w czasie kompilacji, a jedna z gałęzi nigdy nie zostanie podjęta.

Wymień ? :się std::conditional_t, które nie mają tego ograniczenia:

static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;
HolyBlackCat
źródło
15

Problem polega na tym, że wszystkie argumenty operatora warunkowego zostaną ocenione, więc oba commondivs<N,(M-N)>commondivs<(N-M),M> instancja i instancja valzostaną ocenione, a następnie doprowadzą do rekurencyjnej instancji szablonu.

Możesz zastosować constexpr if i umieścić go wconstexpr static funkcji członka.

Jeśli wartość wynosi true , to instrukcja-fałsz jest odrzucana (jeśli występuje), w przeciwnym razie instrukcja-prawda jest odrzucana.

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

RELACJA NA ŻYWO

songyuanyao
źródło
Oceniony czy właśnie utworzony?
Daniel McLaury
@DanielMcLaury Oceniony; nie tylko instancja.
songyuanyao
Wartość ::valmusi być generowana w obu gałęziach, ale nadal jest to tworzenie instancji (szablonu z elementem stałej stałej). Ocena w czasie wykonywania nie występuje ... cóż, oczywiście nie może, ponieważ nigdy się nie kompiluje ...
Bezużyteczne
8

Operator trójskładnikowy nie jest podobny if constexpr: gdy kompilator go zobaczy, musi wygenerować kod dla obu gałęzi. Innymi słowy, w celu utworzenia szablonu commondivs<M, N>kompilator tworzy zarówno szablony, jakcommondivs<N, M - N> i commondivs<N - M, M>.

W przeciwieństwie do tego commondiv(N, M - N)i commondiv(N - M, M)są tłumaczone na dwa wywołania funkcji. Który z nich zostanie podjęty, decyzja zostanie podjęta, kiedy funkcja zostanie faktycznie wywołana.

Dodanie.

HolyBlackCat dał rozwiązanie std::conditional_t. Oto kolejny:

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
źródło
0

Otrzymujesz nieskończoną rekurencję, ponieważ:

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

wcale nie jest programowaniem metatemplate, ponieważ ?:, jak mówi @Eng, nie jestconstexpr .

Chcesz spojrzeć na odpowiedź @ HolyBlackCat.

Paul Evans
źródło
1
To nie pomoże ?:nie jestconstexpr .
Evg
Nie, próbuję. Ta sama nieskończona pętla.
Exxul