Есть ли элегантный способ специализации шаблона на основе одного из его параметров?
Т.е.
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;
}
};
Но это значительно менее элегантно, а также требует создания экземпляров всех шаблонов, даже если аргумент шаблона не требует этого.





Как это:
#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;
}
не с g ++ 4.1.3 meta_programming.cpp: 10: error: явная специализация в структуре области без пространства имен my <N> meta_programming.cpp: 10: error: включающие шаблоны классов не являются явно специализированными
УТОЧНЕНИЕ: я скомпилировал его с помощью Visual C++ 2008, запустил и проверил результаты теста.
Извините, @David Nehme, я не использую g ++. Если у вас есть решение, совместимое с g ++, опубликуйте его, потому что я хотел бы узнать больше о том, что я делаю неправильно.
Я переработал это, чтобы (надеюсь) быть более приемлемым для g ++. Он по-прежнему отлично работает с VC9. @ Дэвид Нехме, вы можете сообщить об успехе / неудаче на g ++?
Я пробовал 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: недопустимый тип в объявлении перед токеном ';'
поместите typename перед f :: value_type. f :: value_type - зависимый тип.
@MattJ, я отредактировал в соответствии с предложением @ litb (я также сократил «числитель» до «число» и «знаменатель» до «деном», чтобы избежать горизонтальной полосы прокрутки). Это помогает?
Я бы вряд ли назвал это элегантным, но вот моя версия вашего кода, использующая только шаблоны для вычислений (вместе с тестовой штукой) -
#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 :)
Я скомпилировал его, запустил и проверил результаты тестов. Я включил всю программу, чтобы другие могли сделать то же самое.