Meta programação

38

Alguém pode me explicar por que o primeiro método de meta-programação de modelos está indo para um loop infinito, mas o segundo é executado corretamente.

#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
fonte
2
O objetivo era usar a meta programação de modelos. constexprnão é uma opção.
Exxul
Adicionada tag c ++ 98 para tornar explícito que constexprnão é uma opção. (Foi introduzido no C ++ 11). Isso invalida as respostas existentes. Exxul, esclareça a qual versão do C ++ você está limitado.
MSalters 26/11/19
Desculpe, removi a tag.
Exxul 27/11/19

Respostas:

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

Essa linha causa instanciação de ambos commondivs<N,(M-N)>::vale commondivs<(N-M),M>::val, mesmo que a condição seja conhecida no momento da compilação e uma das ramificações nunca será executada.

Substitua ? :por std::conditional_t, que não possui esta limitação:

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

O problema é todos os operandos do operador condicional será avaliado, então ambos commondivs<N,(M-N)>e commondivs<(N-M),M>se instanciado e seu valget avaliadas e, em seguida, leva a instanciação de modelo recursivo.

Você pode aplicar constexpr if e colocá-lo em uma constexpr staticfunção de membro.

Se o valor for true, a instrução false será descartada (se presente), caso contrário, a instrução true será descartada.

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

VIVER

songyuanyao
fonte
Avaliada ou apenas instanciada?
precisa
@DanielMcLaury Avaliado; não apenas instanciado.
songyuanyao
O valor de ::valdeve ser gerado nos dois ramos, com certeza, mas isso ainda é instanciação (de um modelo com um membro const estático). A avaliação em tempo de execução não acontece ... bem, obviamente, não pode, pois nunca compila ... #
2633 Useless
8

O operador ternário não é como if constexpr: quando um compilador o vê, ele precisa gerar código para os dois ramos. Em outras palavras, para instanciar um modelo commondivs<M, N>, um compilador instancia os modelos commondivs<N, M - N>e commondivs<N - M, M>.

Em contraste com isso, commondiv(N, M - N)e commondiv(N - M, M)são traduzidos em duas chamadas de função. Qual deles é escolhido, será decidido quando a função for realmente chamada.

Adição.

HolyBlackCat deu uma solução com std::conditional_t. Aqui está outro:

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
fonte
0

Você obtém recursão infinita porque:

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

não é programação de metatemplate ?:, porque , como diz o @Eng, não é constexpr.

Você quer ver a resposta de @ HolyBlackCat.

Paul Evans
fonte
11
Isso não vai ajudar. ?:não é constexpr.
Evg
Não, eu tento. O mesmo loop infinito.
Exxul