Как развернуть короткий цикл в С++
Интересно, как получить что-то вроде этого:
Я знаю, что С#defines нельзя использовать рекурсивно для получения этого эффекта. Но я использую С++, поэтому я предполагаю, что мета-программирование шаблона может быть уместным.
Я знаю, что для этого есть библиотека Boost, но мне нужен только этот "простой" трюк, а Boost слишком "грязный".
Ответы
Ответ 1
Наиболее простым решением для этого является запись цикла, в котором известны начальное и конечное значения:
for(int i = 2; i <= 4; i++) {
a[i]=b[i];
}
Я думаю, что это лучше, чем любая смесь шаблонов /runtime -call: петля, написанная, полностью понятна оптимизатору компиляторов, и нет уровней вызовов функций, чтобы прорыть, чтобы увидеть, что происходит.
Ответ 2
Мета-программирование С++ рекурсивно.
Подумайте о своей проблеме с точки зрения рекурсии. Внедрение терминальных и нетерминальных случаев.
Ваш терминал может быть как 0, так и один. Пределы пропуска в качестве параметров шаблона. Используйте структуру/класс, потому что они позволяют частичную специализацию и другие опрятные вещи:
template<int from, int to>
struct copy {
static void apply(source, destination) {
source[from] = destination[from];
copy<from+1,to>:: apply(source, destination);
}
};
// Terminal case
template<int from>
struct copy<from, from> {
static void apply(source, destination) {
source[from] = destination[from];
}
};
Ответ 3
Возможно, вы можете сделать что-то вроде следующего. В зависимости от вашего компилятора и параметров оптимизации, которые вы используете, вы можете получить тот эффект, который вы ищете.
Помните, что для небольших объектов, таких как char, он может быть медленнее, чем std::copy
или memcpy
, а для более крупных объектов стоимость цикла может быть незначительной по сравнению с копиями, которые происходят в в любом случае.
#include <cstddef>
template<std::size_t base, std::size_t count, class T, class U>
struct copy_helper
{
static void copy(T dst, U src)
{
dst[base] = src[base];
copy_helper<base + 1, count - 1, T, U>::copy(dst, src);
}
};
template<std::size_t base, class T, class U>
struct copy_helper<base, 0, T, U>
{
static void copy(T, U)
{
}
};
template<std::size_t base, std::size_t count, class T, class U>
void copy(T dst, U src)
{
copy_helper<base, count, T, U>::copy(dst, src);
}
template void copy<5, 9, char*, const char*>(char*, const char*);
#include <iostream>
#include <ostream>
int main()
{
const char test2[] = " , World\n";
char test[14] = "Hello";
copy<5, 9>(test, test2);
std::cout << test;
return 0;
}
Ответ 4
Важно понимать, что компилятор очень умный, и что обманывать его для разворачивания циклов с использованием метапрограммирования шаблонов, вероятно, заставит вас вернуться к дальнейшему продвижению.
Чтобы получить нижнюю часть ваших оптимизаций: следите за разборкой. Это, надеюсь, научит вас больше, чем бросать шаблоны в проблему.
И обратите внимание, как сказал Йоханнес: если компилятор увидит, что вы запускаете цикл для фиксированного количества раз (или фиксированного кратного раза, например 4x-переменной), он может создать код, очень близкий к оптимальному.
Ответ 5
От http://www.sgi.com/tech/stl/Vector.html:
template <class InputIterator>
vector(InputIterator, InputIterator)
Creates a vector with a copy of a range.
Ответ 6
Он не использует шаблоны, и это не "полная" разворачивание, но вы можете частично развернуть цикл с чем-то вроде этого:
void copy (SomeType* a, SomeType* b, int start_index, int num_items) {
int i = start_index;
while (num_items > 4) {
a[i+0] = b[i+0];
a[i+1] = b[i+1];
a[i+2] = b[i+2];
a[i+3] = b[i+3];
i += 4;
num_items -= 4;
}
while (num_items > 0) {
a[i] = b[i];
++i;
--num_items;
}
}
Теперь в этом конкретном примере дополнительные вычисления, вероятно, перевешивают преимущества только одновременного разворачивания четырех элементов. Вы должны получать все большее преимущество от возрастающего числа элементов внутри верхнего цикла (во всей функции, заменяйте 4
тем же количеством элементов, которые вы копируете внутри каждой развернутой вручную итерации).
Ответ 7
template <int begin, int end> struct copy_;
template <int end> struct copy_<end, end> {
template <typename T> static void execute(T& a, T& b)
{
a[end] = b[end];
}
};
template <int begin, int end> struct copy_<begin, end> {
template <typename T> static void execute(T& a, T& b)
{
a[begin] = b[begin];
copy_<begin+1, end>::execute(a, b);
}
};
template <int begin, int how_many> struct copy {
template <typename T> static void execute(T& a, T& b)
{
copy_<begin, begin+how_many-1>::execute(a, b);
}
};
copy<2, 3>::execute(a, b);
Ответ 8
Развертывание цикла с использованием метапрограмм может использоваться для создания constexpr (я не измерял время). У меня есть пример, где его можно использовать для создания функциональной комбинации (n, k) a cosntexpr:
template <size_t N> struct iterate_forward {
template <class operation> static auto eval(const operation & op)
{
iterate_forward<N-1>::eval(op);
return op(N-1);
}
};
template <> struct iterate_forward<1>
{
template <class operation> static auto eval(const operation & op)
{ return op(0); }
};
template <> struct iterate_forward<0>
{
template <class operation> static auto eval(const operation & op) {}
};
template <size_t N, size_t K> constexpr size_t COMB()
{
struct ratio
{
size_t operator () (size_t i) const { return (res *= (N-i) / (i+1)); }
mutable size_t res = 1;
};
return iterate_forward<K>::eval(ratio());
}
Ответ 9
#define tl template
#define tn typename
#define st struct
#define cl class
#define us using
template<tn A> st Typ { using type = A; };
tl<tn T> using GetT = tn T::type;
tl<tn F, tn ...As> us apply = tn F::tl apply<As...>;
tl<tn, tn, tn ...> st LoopElements;
tl<tn, tn> st Loop;
tl<tn, tn, tn> st VLoop_i;
tl<tn Sq, tn MF> us VLoop = VLoop_i<GetT<Sq>, Sq, MF>;
//
// TY
//
template<tn T> struct Ty {
template<T ...> struct Pack : Typ<T> {};
tl<tn ...> st Concat_i; tl<tn ...P> us Concat = GetT<Concat_i<P...>>;
tl<T, int64_t> st Seq_i; tl<T f, T t> us Seq = GetT<Seq_i<f, ((int64_t)(t - f))>>; tl<int64_t, tn> st add;
template<tl<T ...> tn C, T ...vs, tl<T ...> tn D, T ...ws, tn ...R> st Concat_i<C<vs...>, D<ws...>, R...> : Typ<Concat<C<vs..., ws...>, R...> >{};
template<tl<T ...> tn C, T ...vs> st Concat_i<C<vs...>> : Typ<C<vs...> >{};
template<int64_t x, T ...vs> struct add<x, Pack<vs...>> : Typ<Pack<((T)(vs + x))...>> {};
template<T f, int64_t c> class Seq_i {
using A = tn Seq_i<f, c/2>::type;
using B = tn add<c/2, A>::type;
using C = tn Seq_i<f + c / 2 * 2, c & 1>::type;
public:
using type = Concat<A, B, C>;
};
tl<T f> st Seq_i<f, 0> : Typ<Pack<>> {};
tl<T f> st Seq_i<f, 1> : Typ<Pack<f>> {};
tl<T f> st Seq_i<f, -1> : Typ<Pack<f>> {};
};
//
// LOOP
template<size_t i, tn F, tn T> struct LoopElement { LoopElement() { apply<F, T>(); }; };
template<size_t ...is, tn F, tn ...P> struct LoopElements<Ty<size_t>::Pack<is...>, F, P...> : LoopElement<is, F, P>... {};
template<tn F, tl<tn ...> cl C, tn ...P> struct Loop<F, C<P...>> : LoopElements<Ty<size_t>::Seq<0, sizeof...(P)>, F, P...> {};
template<tn T, tl<T ...> cl ST, T ...vs, tn MF> struct VLoop_i<T, ST<vs...>, MF> : LoopElements<Ty<size_t>::Seq<0, sizeof...(vs)>, MF, Val<T, vs>...> {};