Мета-програмування шаблонів


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, тоді оператор-false відкидається (якщо він присутній), інакше твердження-істина відкидається.

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 Оцінено; не просто примірник.
songyuanyao

Значення ::valмає бути згенеровано на обох гілках, але це все-таки інстанція (для шаблону зі статичним членом const). Оцінка під час виконання не відбувається ... ну, очевидно, не може, оскільки ніколи не збирається ...
Марно

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.