Развертывание циклов с использованием шаблонов в C ++ с частичной специализацией - PullRequest
6 голосов
/ 04 марта 2011

Я пытаюсь использовать шаблоны для развертывания цикла в C ++ следующим образом.

#include <iostream>

template< class T, T i >
struct printDown {
    static void run(void) {
        std::cout << i << "\n";
        printDown< T, i - 1 >::run();
    }
};

template< class T >
struct printDown< T, 0 > {
    static void run(void) {
        std::cout << 0 << "\n";
    }
};

int main(void) {
    printDown< int, 10 >::run();
    return 0;
}

Когда я компилирую w / g ++ 3.4.4 в Cygwin, я получаю следующую ошибку.

tmp.cpp: 12: ошибка: тип T' of template argument 0 'зависит от параметров шаблона

Что я делаю не так?Нужно ли как-то аннотировать 0, чтобы сказать, что он типа T?

Заранее спасибо.

Ответы [ 6 ]

5 голосов
/ 04 марта 2011

Вы пробовали int i вместо T i?

4 голосов
/ 04 марта 2011

Почему это происходит? От 14.5.5 / 8

- тип параметра шаблона соответствующий специализированному нетипичный аргумент не должен быть зависит от параметра специализация. [Пример:

template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error

- конец примера]

Следовательно, когда вы применяете частичную специализацию, тип 0 - это T (зависит от параметра специализации). Есть два варианта: один - сделать его независимым, например, изменить T i на int i, а второй - применить явную специализацию, а не частичную специализацию.

Оба решения были выданы другими, поэтому я не собираюсь публиковать их здесь. По крайней мере, вы знаете причину. Это определяется стандартом.

1 голос
/ 04 марта 2011

Как указывает phooji, ваша реализация страдает от небольшой проблемы: она быстро генерирует длинный список вызовов, что заставит компиляторы быстро подавиться.

Вы можете обойти эту проблему, внедрив немного большесложная версия, использующая бинарную декомпозицию.Я также сделаю это универсальным для функтора, потому что я ленивый.

// Signature
template <Functor F, unsigned N>
struct UnrolledLoop;

Нам нужен вспомогательный шаблон, который сохраняет смещение параметра для передачи

template <Functor F, unsigned N, unsigned OffSet>
struct UnrolledImpl;

template <Functor F, unsigned OffSet>
struct UnrolledImpl<F, 0, OffSet>
{
  static F run(F f) { return f; }
};

template <Functor F, unsigned OffSet>
struct UnrolledImpl<F, 1, OffSet>
{
  static F run(F f) { f(OffSet); return f; }
};

template <Functor F, unsigned N, unsigned OffSet>
struct UnrolledImpl
{
  static F run(F f) {
    F f2 = UnrolledImpl<F, N/2, OffSet>::run(f);
    return UnrolledImpl<F, N - N/2, OffSet + N/2>::run(f2);
  }
};

ИВы можете реализовать UnrolledLoop просто:

template <Functor F, unsigned N>
struct UnrolledLoop
{
  static F run(F f) { return UnrolledImpl<F, N, 0>::run(f); }
}

Обратите внимание, что вы можете предоставить специализацию для большего количества значений N (например, 3, 4) для компилятора.

0 голосов
/ 04 марта 2011

Вы можете сделать параметр параметром типа, чтобы обойти это

template< bool > struct bool_ { };

template< class T, T i, typename = bool_<true> >
struct printDown {
    static void run(void) {
        std::cout << i << "\n";
        printDown< T, i - 1 >::run();
    }
};

template< class T, T i >
struct printDown< T, i, bool_<i == 0> > {
    static void run(void) {
        std::cout << 0 << "\n";
    }
};

int main(void) {
    printDown< int, 10 >::run();
    return 0;
}

Таким образом, вы можете указать любые условия, которые вы хотите в частичных специализациях.

0 голосов
/ 04 марта 2011

Только что это выяснил. Видимо, можно сделать что-то вроде этого.

template< class T, T i, bool b = (i == 0) >
struct printDown {
    static void run(void) {
        std::cout << i << "\n";
        printDown< T, i - 1 >::run();
    }
};

template< class T, T i >
struct printDown< T, i, true > {
    static void run(void) {
        std::cout << 0 << "\n";
    }
};

Я понятия не имел, что можно сделать. Очень прологично и очень мило.

0 голосов
/ 04 марта 2011

Как насчет добавления этого в ваш пример:

template struct printDown< int, 0 >{
    static void run(void) {
    std::cout << 0 << "\n";
} };

Компилятор не может автоматически приводить 0 к int, не зная заранее тип T.

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