Использование метапрограммирования для вычисления log2, но `компиляция прекращена` - PullRequest
0 голосов
/ 21 октября 2018

Я новичок в TMP, и я написал программу, использующую метапрограммирование для вычисления log2 .Я написал шаблон структуры power2 для расчета мощности, класс шаблона log2arr с массивом для сохранения результата и встроенную структуру шаблона log2 для вычисления значений log2.

#include <iostream>

template <int i>
struct power2
{
    enum
    {
        value = (1 << i)
    };
};

template <int n>
class log2arr
{
    int arr[n];

    template <int i>
    struct log2
    {
        log2<i - 1> _arr;
        enum
        {
            value = log2<i - 1>::value + (power2<log2<i - 1>::value>::value == i)
        };
        void print()
        {
            _arr.print();
            std::cout << value << std::endl;
        }
        void set()
        {
            _arr.set();
            arr[i] = value;
        }
    };

    template <>
    struct log2<1>
    {
        enum
        {
            value = 1
        };
        void print() {}
        void set() {}
    };

  public:
    int *getArr()
    {
        log2<n>().set();
        return arr;
    }
};

int main()
{
    log2arr<4> a = log2arr<4>();
    for(auto i : a.getArr()){
        cout << i;
    }
}

Но компилятор только сказал мне compilation terminated.

Что это значит?Как я могу решить это?

Любая помощь будет принята с благодарностью.

1 Ответ

0 голосов
/ 21 октября 2018

В вашем коде есть некоторые проблемы, и я покажу некоторые из них в произвольном порядке.

(1) полные специализации не допускаются внутри структуры / класса;поэтому вы не можете полностью специализироваться log2 внутри log2arr.

Вы можете мигрировать log2 за пределы log2arr или, если вы действительно хотите сохранить его внутри log2arr, вы можете преобразовать полную специализациюв эквивалентной частичной специализации (легально внутри структуры / класса);Например, следующим образом

  template <int I, typename = std::true_type>
  struct log2
   {
     // body of the struct
   };

  template <int I>
  struct log2<I, std::integral_constant<bool, I == 1>>
   {
     // body of the struct specialization
   };

(2) если вы вернете int * из getArr(), вы потеряете информацию о массиве внутри log2Arr class;поэтому не работает диапазон, основанный на цикле (for(auto i : a.getArr())).

К сожалению, вы не можете вернуть массив в стиле C (вы не можете вернуть arr сам).

Но вы используете C ++ 11 или новее (вы пометили только C ++, но вы используете диапазон, основанный на цикле, поэтому вы используете по крайней мере C ++ 11), поэтому я настоятельно рекомендую вам определить arr какstd::array<int, N>, не как массив в стиле C (не int arr[N]).И я настоятельно рекомендую вам вернуть ссылку на саму arr (с помощью std::array вы можете сделать это)

   private:
      using arrT = std::array<int, N>;

      arrT arr {};

      // ...

   public:

      arrT & getArr ()
       { /* ... */ return arr; }

, и я также предлагаю добавить getArr() для const объектов

  arrT const & getArr () const
   { /* ... */ return arr; }

(3) вы не можете управлять массивом arr (не статическим членом log2Arr) внутри метода встроенной структуры log2

    void set()
    {
        _arr.set();
        arr[i] = value; // <--- arr is inaccessible
    }

Aвозможное решение - передать arr в качестве ссылки, поэтому

    void set (arrT & a) // arrT = std::array<int, N>
     {
        _arr.set(a);
        a[i] = value;
     }

и (в log2<1>)

    void set (arrT &) {}

Очевидно, вам нужно вызвать set(), передав arr в качестве аргументаТак что

log2<N>().set(arr)

(4) инициализация arr внутри getArr() - плохая идея (ИМХО), потому что вы инициализируете arr каждый раз, когда звоните getArr().

Более того: вы не можете использовать arr внутри другого метода (если вы хотите добавить другой) без инициализации его внутри другого метода.

Предложение: инициализировать arr, один раз для всех, внутриявный конструктор;Например,

  log2arr ()
   { log2<N>().set(arr); }

, так что ваши getArr() методы просто становятся

  arrT & getArr ()
   { return arr; }

  arrT const & getArr () const
   { return arr; }

(5) с log2<I>, которые инициализируют arr[I] и log<1>, которые ничего не инициализируют, ваш int arr[N] содержит значения arr[0] и arr[1], которые не являются инициализированными.

Вы можете инициализировать эти значения, записывая ноль

  int arr[N] {};

или (используя std::array<int, N>)

  using arrT = std::array<int, N>;

  arrT arr {};
  //       ^^  <--- initialize all to zero

но вы должны решить, как инициализировать в arr[0] и arr[1]

(6) нет необходимости инициализировать a следующим образом

log2arr<4> a = log2arr<4>();

Вы можете просто написать

log2arr<4> a;

--------------------------------------

Ниже приведен модифицированный код

#include <array>
#include <iostream>

template <int I>
struct power2
 { enum { value = (1 << I) }; };

template <int N>
class log2arr
 {
   private:
      using arrT = std::array<int, N>;

      arrT arr {};

      template <int I, typename = std::true_type>
      struct log2
       {
         log2<I-1> _arr;

         enum { value =   log2<I-1>::value
                        + (power2<log2<I-1>::value>::value == I) };

         void print ()
          { _arr.print(); std::cout << value << std::endl; }

         void set (arrT & a)
          { _arr.set(a); a[I] = value; }
       };

      template <int I>
      struct log2<I, std::integral_constant<bool, I == 1>>
       {
         enum { value = 1 };

         void print() {}
         void set(arrT &) {}
       };

   public:
      log2arr ()
       { log2<N>().set(arr); }

      arrT & getArr ()
       { return arr; }

      arrT const & getArr () const
       { return arr; }
 };

int main ()
 {
   log2arr<4> a;

   for ( auto i : a.getArr() )
      std::cout << i;

   std::cout << std::endl;
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...