Шаблон метапрограммирования

38

Может кто-нибудь объяснить мне, почему первый способ метапрограммирования шаблона идет в бесконечный цикл, а второй работает правильно.

#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
источник
2
Целью было использование шаблонов метапрограммирования. constexprэто не вариант.
Exxul
Добавлен тег c ++ 98, чтобы сделать явным, что constexprэто не вариант. (Это было введено в C ++ 11). Это делает недействительными существующие ответы. Exxul, пожалуйста, уточните, какой версией C ++ вы ограничены.
MSalters
Извините, я удалил тег.
Exxul

Ответы:

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

Эта строка вызывает создание экземпляров обоих commondivs<N,(M-N)>::valи commondivs<(N-M),M>::valдаже если условие известно во время компиляции и одна из ветвей никогда не будет взята.

Замените ? :на std::conditional_t, который не имеет этого ограничения:

static const int val = std::conditional_t<N < M, commondivs<N,(M-N)>, commondivs<(N-M),M>>::val;
HolyBlackCat
источник
15

Проблема в том, что все операнды условного оператора будут оцениваться, поэтому оба commondivs<N,(M-N)> и commondivs<(N-M),M>получить экземпляр и их valполучить оценку , а затем приводит к рекурсивной шаблона конкретизации.

Вы можете применить constexpr, если и поместить его в функцию- constexpr staticчлен.

Если значение равно 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();
};

ЖИТЬ

songyuanyao
источник
Оценивается или просто создается?
Даниэль МакЛори
@DanielMcLaury Evaluated; не просто создан.
songyuanyao
Значение ::valдолжно быть обязательно сгенерировано в обеих ветвях, но это все еще экземпляр (для шаблона со статическим константным членом). Оценка во время выполнения не происходит ... ну, она, очевидно, не может, так как никогда не компилируется ...
Бесполезно
8

Тернарный оператор не похож if constexpr: когда компилятор видит его, он должен генерировать код для обеих ветвей. Другими словами, чтобы создать экземпляр шаблона commondivs<M, N>, компилятор создает оба шаблона.commondivs<N, M - N> и commondivs<N - M, M>.

В отличие от этого commondiv(N, M - N)иcommondiv(N - M, M) переводятся в два вызова функций. Какой из них взят, будет решено, когда функция будет фактически вызвана.

Дополнение.

HolyBlackCat дал решение с std::conditional_t. Вот еще один:

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
источник
0

Вы получаете бесконечную рекурсию, потому что:

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

не программирование метатемплей вообще, потому что ?:, как говорит @Eng, неconstexpr .

Вы хотите посмотреть на ответ @ HolyBlackCat.

Пол Эванс
источник
1
Это не поможет ?:не constexpr.
Evg
Нет, я пробую это. Тот же бесконечный цикл.
Exxul