Элегантная специализация шаблона

Есть ли элегантный способ специализации шаблона на основе одного из его параметров?

Т.е.

template<int N> struct Junk {
    static int foo() {
        // stuff
        return Junk<N - 1>::foo();
    }
};

// compile error: template argument '(size * 5)' involves template parameter(s)
template<int N> struct Junk<N*5> {
    static int foo() {
        // stuff
        return N;
    }
};

template<> struct Junk<0> {
    static int foo() {
        // stuff
        return 0;
    }
};

Т.е. Я пытаюсь специализировать шаблон на основе параметра, делимого на 5. Единственный способ, которым я могу это сделать, - это как показано ниже:

template<int N> struct JunkDivisibleBy5 {
    static int foo() {
        // stuff
        return N;
    }
};

template<int N> struct Junk {
    static int foo() {
        // stuff
        if ((N - 1) % 5 == 0 && N != 1)
            return JunkDivisibleBy5<N - 1>::foo();
        else
            return Junk<N - 1>::foo();
    }
};


template<> struct Junk<0> {
    static int foo() {
        // stuff
        return 0;
    }
};

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
2 981
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Как это:

#include <iostream>
using namespace std;

template < typename T, T N, T D >
struct fraction {
    typedef T value_type;
    static const value_type num = N;
    static const value_type denom = D;
    static const bool is_div = (num % denom == 0);
};

template< typename T, T N, T D, bool P >
struct do_if {
    static void op() { cout << N << " NOT divisible by " << D << endl; }
};

template< typename T, T N, T D >
struct do_if< T, N, D, true > {
    static void op() { cout << N << " divisible by " << D << endl; }
};

template < int N >
void foo() {
    typedef fraction< int, N, 5 > f;
    do_if< typename f::value_type, f::num, f::denom, f::is_div >::op();
}

int main() {
    foo< -5 >();
    foo< -1 >();
    foo< 0 >();
    foo< 1 >();
    foo< 5 >();
    foo< 10000005 >();
    return 0;
}

Я скомпилировал его, запустил и проверил результаты тестов. Я включил всю программу, чтобы другие могли сделать то же самое.

jwfearn 06.11.2008 20:17

не с g ++ 4.1.3 meta_programming.cpp: 10: error: явная специализация в структуре области без пространства имен my <N> meta_programming.cpp: 10: error: включающие шаблоны классов не являются явно специализированными

David Nehme 06.11.2008 20:32

УТОЧНЕНИЕ: я скомпилировал его с помощью Visual C++ 2008, запустил и проверил результаты теста.

jwfearn 06.11.2008 21:00

Извините, @David Nehme, я не использую g ++. Если у вас есть решение, совместимое с g ++, опубликуйте его, потому что я хотел бы узнать больше о том, что я делаю неправильно.

jwfearn 06.11.2008 21:07

Я переработал это, чтобы (надеюсь) быть более приемлемым для g ++. Он по-прежнему отлично работает с VC9. @ Дэвид Нехме, вы можете сообщить об успехе / неудаче на g ++?

jwfearn 06.11.2008 23:21

Я пробовал w / g ++ 4.3.3: no dice :( ~ $ g ++ metaprog.cpp -o metaprog metaprog.cpp: в функции 'void foo ()': metaprog.cpp: 25: error: несоответствие типа / значения в аргументе 1 в списке параметров шаблона для 'template <class T, TN, TD, bool P> struct do_if' metaprog.cpp: 25: error: ожидался тип, получил 'f :: value_type' metaprog.cpp: 25: error: '< type error> 'не является допустимым типом для параметра константы шаблона metaprog.cpp: 25: error:' <type error> 'не является допустимым типом для параметра константы шаблона metaprog.cpp: 25: error: недопустимый тип в объявлении перед токеном ';'

Matt J 31.05.2009 09:55

поместите typename перед f :: value_type. f :: value_type - зависимый тип.

Johannes Schaub - litb 31.05.2009 10:08

@MattJ, я отредактировал в соответствии с предложением @ litb (я также сократил «числитель» до «число» и «знаменатель» до «деном», чтобы избежать горизонтальной полосы прокрутки). Это помогает?

jwfearn 31.05.2009 19:52

Я бы вряд ли назвал это элегантным, но вот моя версия вашего кода, использующая только шаблоны для вычислений (вместе с тестовой штукой) -

#include <iostream>

template < int N > struct JunkDivBy5 {
    static int foo() {
        return N;
    }
};

template < int N > struct Junk {
    template < int N1 > struct _JunkCond {
        enum { val = ( N1 != 1 && ( N1 - 1 ) % 5 == 0 ) ? 1 : 0 };
    };

    template < int M, int N1 > struct _JunkBranch { /* Error */ };

    template < int N1 > struct _JunkBranch< 1, N1 > {
        typedef JunkDivBy5< N1 - 1 > Type;
    };

    template < int N1 > struct _JunkBranch< 0, N1 > {
        typedef Junk< N1 - 1 > Type;
    };

    static int foo() {
        return _JunkBranch< _JunkCond< N >::val, N >::Type::foo();
    }
};

template <> struct Junk< 0 > {
    static int foo() {
        return 0;
    }
};

int main( int argc, char *argv[] ) {
    std::cout << Junk< 0 >::foo() << std::endl;
    std::cout << Junk< 5 >::foo() << std::endl;
    std::cout << Junk< 7 >::foo() << std::endl;
    std::cout << Junk< 25 >::foo() << std::endl;
}

Все расчеты можно было произвести во время компиляции:

#include <iostream>

template<int N> struct Junk {
    enum { IsDivisibleBy5 = (N % 5 == 0) };
    template<bool D> struct JunkInternal {
        enum { Result = Junk<N-1>::Result };
    };
    template<> struct JunkInternal<true> {
        enum { Result = N };
    };
    enum { Result = JunkInternal<IsDivisibleBy5>::Result };
};

int main(int, char**)
{
    std::cout << Junk< 0 >::Result << std::endl;
    std::cout << Junk< 7 >::Result << std::endl;
    std::cout << Junk< 10 >::Result << std::endl;

    return 0;
}

Наследование работает неплохо:

template<int N> struct Junk : private JunkBase < N % 5 > { };

template<int N> struct JunkBase {
    static int foo() {
        // stuff
        return Junk<N - 1>::foo();
    }
};
template< > struct JunkBase<0> {
    static int foo() {
        return 0;
    }
};

Вам может потребоваться передать N в JunkBase :: foo, если вам тоже нужно N / 5.

Код

template<int A, bool = !(A % 5)>
struct select : select<A-1> { };

template<int A>
struct select<A, true> { static int const value = A; };

template<>
struct select<0, true> { static int const value = 0; };

int main() {
    std::cout << select<1>::value; // 0
    std::cout << select<7>::value; // 5
    std::cout << select<10>::value; // 10
}

Сохраните переменную делителя

template<int A, int D, bool = !(A % D)>
struct select : select<A-1, D> { };

template<int A, int D>
struct select<A, D, true> { static int const value = A; };

template<int D>
struct select<0, D, true> { static int const value = 0; };

int main() {
    std::cout << select<1, 3>::value; // 0
    std::cout << select<7, 3>::value; // 6
    std::cout << select<10, 3>::value; // 9
}

Используя шаблоны языка программирования D, можно было бы записать это как:

struct Junk(int N)
{
    static int foo()
    {
        static if (N == 0)
            return 0;
        else static if ((N % 5) == 0)
            return N;
        else
            return Junk!(N - 1).foo();
    }
}

статические if выполняются во время компиляции.

Мне не следует голосовать за это, но я сделал это, потому что это внезапно заставило меня захотеть выучить D :)

Mark K Cowan 10.09.2015 14:09

Другие вопросы по теме