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


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


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;

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

ЖИТЬ


Оценивается или просто создается?
Даниэль МакЛори

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

0

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

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

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

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


1
Это не поможет ?:не constexpr.
Evg

Нет, я пробую это. Тот же бесконечный цикл.
Exxul
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.