Ошибка при создании экземпляра рекурсивного шаблона класса для перегруженного оператора умножения в C

#c #templates #operator-overloading

#c #шаблоны #перегрузка оператора

Вопрос:

Я пытаюсь реализовать шаблонный класс для очень специального блока, называемого H Box.
Он содержит вектор чисел, размер которого всегда равен степени 2. По сути, это так, когда дело доходит до структуры — она содержит только массив и имеет [] перегруженный оператор для доступа к его элементам. Дело в том, что у него очень причудливые правила умножения: произведение двух блоков H сводится к разделению векторов на левую и правую половины, созданию блоков меньшего размера, их перекрестному умножению и суммированию результатов в конце (подробности в коде ниже).). Таким образом, проблема заключается в том, что оператор умножения перегружен как рекурсивная функция, которая генерирует все меньшие и меньшие блоки.

У меня был код, работающий для не шаблонной версии моего класса. Однако после того, как я перешел к шаблону, я не могу сделать это правильно…

 template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> amp;H1,
    const HBox<T, size> amp;H2
) {
    // recursion base:
    if (size == 1) {
        T temparr[] = { H1[0] * H2[0] };
        HBox<T, 1> H_(temparr);
        return H_;
    }
    // shared objects:
    const unsigned int halfsize = size / 2;
    T* temparr = new T[size];
    // construct helper objects:
    for (unsigned int i=0; i < halfsize; i  ) temparr[i] = H1[i];
    HBox<T, halfsize> H1a(temparr);
    for (unsigned int i=0; i < halfsize; i  ) temparr[i] = H1[i halfsize];
    HBox<T, halfsize> H1b(temparr);
    for (unsigned int i=0; i < halfsize; i  ) temparr[i] = H2[i];
    HBox<T, halfsize> H2a(temparr);
    for (unsigned int i=0; i < halfsize; i  ) temparr[i] = H2[i halfsize];
    HBox<T, halfsize> H2b(temparr);
    // multiply recursively:
    HBox<T, halfsize> H1a2a = H1a * H2a;
    HBox<T, halfsize> H2b1b = H2b * H1b;
    HBox<T, halfsize> H2b1a = H2b * H1a;
    HBox<T, halfsize> H1b2a = H1b * H2a;
    // construct the final object
    HBox<T, halfsize> Ha = H1a2a   H2b1b;
    HBox<T, halfsize> Hb = H2b1a   H1b2a;
    for (unsigned int i=0; i < halfsize; i  ) temparr[i] = Ha[i];
    for (unsigned int i=0; i < halfsize; i  ) temparr[i halfsize] = Hb[i];
    HBox<T, size> H(temparr);
    delete[] temparr;
    return H;
}
 

Я компилирую простую тестовую программу, которая содержит умножение двух четырехэлементных float блоков ( A * B ) с:

 Run g   -O0 -Wall --std=c  14 -o test test.cpp
 

И я получаю следующую ошибку:

 In file included from test.cpp:17:
HBox/HBox.hpp: In instantiation of ‘HBox<T, size> operator*(const HBox<T, size>amp;, const HBox<T, size>amp;) [with T = float; unsigned int size = 4]’:
test.cpp:266:44:   required from here
HBox/HBox.hpp:276:16: error: could not convert ‘H_’ from ‘HBox<[...],1>’ to ‘HBox<[...],4>’
  276 |         return H_;
      |                ^~
      |                |
      |                HBox<[...],1>
Error: Process completed with exit code 1.
 

Что пошло не так? Мне кажется, что здесь все должно быть хорошо…

Ответ №1:

При использовании рекурсии шаблона вы не можете предоставить базовый вариант как простой if . Причина этого в том, что вся функция должна иметь возможность компилироваться, даже если какой-то код недоступен. Простого if (false) недостаточно.

Чтобы исправить это, вы можете использовать C 17 if constexpr или частичную специализацию шаблона:

 // using if constexpr
template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> amp;H1,
    const HBox<T, size> amp;H2
) {
    if constexpr (size == 1) {
        // handle base case here
    } else {
        // handle recursive case here
    }
}
 

или

 // using partial template specialization
template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> amp;H1,
    const HBox<T, size> amp;H2
) {
    // handle recursive case here
}

template <typename T>
HBox<T, 1> operator*<T, 1>( // partial specialization for size == 1
    const HBox<T, 1> amp;H1,
    const HBox<T, 1> amp;H2
) {
    // handle base case here
}
 

Ответ №2:

Даже если ваша программа никогда не перейдет в оператор if, когда n ! = 1, ваш компилятор пытается скомпилировать такую ветку. Таким образом, вы можете возвращать HBox<T, 1> из функции, которая должна возвращаться HBox<T, 4> . Сначала просто попробуйте добавить constexpr в if

 if constexpr (size == 1)...
 

Если это не помогло, попробуйте использовать этот код

 HBox<T, size> operator*(const HBox<T, size> amp;H1, const HBox<T, size> amp;H2);

template <typename T>
HBox<T, 1> operator*(const HBox<T, 1> amp;H1, const HBox<T, 1> amp;H2) {
  T temparr[] = {H1[0] * H2[0]};
  HBox<T, 1> H_(temparr);
  return H_;
}

template <typename T, const unsigned int size>
HBox<T, size> operator*(const HBox<T, size> amp;H1, const HBox<T, size> amp;H2) {
  // shared objects:
  const unsigned int halfsize = size / 2;
  T *temparr = new T[size];
  // construct helper objects:
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i] = H1[i];
  HBox<T, halfsize> H1a(temparr);
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i] = H1[i   halfsize];
  HBox<T, halfsize> H1b(temparr);
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i] = H2[i];
  HBox<T, halfsize> H2a(temparr);
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i] = H2[i   halfsize];
  HBox<T, halfsize> H2b(temparr);
  // multiply recursively:
  HBox<T, halfsize> H1a2a = H1a * H2a;
  HBox<T, halfsize> H2b1b = H2b * H1b;
  HBox<T, halfsize> H2b1a = H2b * H1a;
  HBox<T, halfsize> H1b2a = H1b * H2a;
  // construct the final object
  HBox<T, halfsize> Ha = H1a2a   H2b1b;
  HBox<T, halfsize> Hb = H2b1a   H1b2a;
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i] = Ha[i];
  for (unsigned int i = 0; i < halfsize; i  ) temparr[i   halfsize] = Hb[i];
  HBox<T, size> H(temparr);
  delete[] temparr;
  return H;
}```