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