[C++] Явная специализация шаблона сразу на несксолько типов

Serpent555

Проблема следующая. Есть шаблонная функция, есть ее шаблонное определение. Имеется явная специализация на некоторый тип. Хочется сделать точно такую же явную специализацию на другой тип и не копипастить при этом код. То есть, в общем случае, я хочу описать другое шаблонное определение для некоторого подмножества типов.
То есть можно легко сделать что-нибудь такое:

template<class T>
void foo(T& x)
{
x++;
}

template<>
void foo<int>(int& x)
{
x--;
}

template<>
void foo<double>(double& x)
{
x--;
}

А хочется что-нибудь типа такого:

template<class T>
void foo(T& x)
{
x++;
}

template<class T>
void foo<int || double>(T& x)
{
x--;
}

Понятно, что второй пример крайне искусственен, но идея, думаю, ясна. У меня пока есть только одна мысль - создать макрос с параметром и сделать две подстановки. Однако для больших объемов (а если родилась такая проблема, то они таковыми и будут) такой макрос будет выглядеть крайне убого.
Какие еще есть идеи? Существуют ли вообще какие-то средства задания кроме шаблонного описания (template<class T>) и явной специализации (template<>)?

Dasar

template<class T>
void foo<int || double>(T& x)
{
x--;
}
смотри boost, там была либа для метапрограммирования
т.е. что-то было для того, чтобы записать выражение типа

void foo<or<is_type<T,int>, is_type<T,double>>(T& x)
{
x--;
}

rosali

такие вещи делаются через traits-ы.

[xoft ~]$ cat traits.cpp

#include <iostream>
using namespace std;

class V0{};
class V1{};

template<class C>
struct FooTraits {
typedef V0 Traits;
};

template<>
struct FooTraits<int> {
typedef V1 Traits;
};

template<>
struct FooTraits<double> {
typedef V1 Traits;
};

template<class C>
struct FooImpl;

template<class T>
void foo(const T & x)
{
FooImpl<typename FooTraits<T>::Traits>x);
}


template<>
struct FooImpl<V0>
{
template<class T>
void operatorconst T & x) {
cout << x+1 << endl;
}
};

template<>
struct FooImpl<V1>
{
template<class T>
void operatorconst T & x) {
cout << x-1 << endl;
}
};

int main
{
foo(int(5;
foo(double(5;
foo(long(5;
}

[xoft ~]$ ./traits
4
4
6

Andbar

как вариант - шаблон функции с двумя параметрами, второй из которых будет фейковым.
template<int v>
class Int2Type
{
enum { value = v };
};

template<class T>
struct IntOrDouble
{
enum { value = 0 };
};

template<>
struct IntOrDouble<int>
{
enum { value = 1 };
};

template<>
struct IntOrDouble<double>
{
enum { value = 1 };
};

template<class T>
inline void foo(T&x)
{
foo_Impl(x, Int2Type<IntOrDouble<T>::value>
}


template<class T>
void foo_Impl(T& x, Int2Type<0>)
{
x++;
}

template<class T>
void foo_Impl(T& x, Int2Type<1>)
{
x--;
}


#include<stdio.h>
int main
{
int i = 1;
double d = 1.0;
unsigned int u = 1;
printf("%d %lf %u\n", i, d, u);
foo(i);
foo(d);
foo(u);
printf("%d %lf %u\n", i, d, u);
}

Serpent555

Круто, все три варианта подходят. Сейчас только остается решить, что будет лучше: тащить в проект boost ради такого дела, писать инфернальные traits, или же придумать какой-нить walkaround, чтобы вообще избежать этой ситуации... Буду думать. Спасибо.

yroslavasako

в бусте скорее всего ещё более инферальная реалзиация, чем traits. Просто она простому взгляду незаметна

Serpent555

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

Serpent555

Да, есть еще один похожий вопрос. Будут ли какие-нибудь дополнительные идеи, если, например, нужно выполнить шаблонное определение для случая, когда совпадают типы шаблонного класса и шаблонной функции внутри него? То есть

template<class T> class A
{
template<class U> void foo { cout << 1; }
}

template<class T>
template<class U> // Только для случаев U == T
void A<T>::foo { cout << 2; }

Andbar

template<class T> template<class U> // Только для случаев U == T void A<T>::foo { cout << 2; }
template<class T>
template<>void A<T>::foo<T> { cout << 2; }
:confused:

Serpent555

Нет, не скомпилится.
Хотя стоп, нет, компилится, ты прав, только писать надо не так. Сейчас надо понять, как писать это вне тела класса, но точно можно сделать так:

template<class T> class A
{
template<class U> void foo { cout << 1; }

template<> void foo<T> {cout << 2; }
}

Serab

Сейчас надо понять, как писать это вне тела класса
Не факт, что вообще возможно. А очень ли оно надо?

Andbar

В стандарте, в разделе 14.5.2, есть только такой вариант:
A local class shall not have member templates. Access control rules (clause 11) apply to member template
names. A destructor shall not be a member template. A normal (non-template) member function with a
given name and type and a member function template of the same name, which could be used to generate a
specialization of the same type, can both be declared in a class. When both exist, a use of that name and
type refers to the non-template member unless an explicit template argument list is supplied. [Example:
template <class T> struct A {
void f(int);
template <class T2> void f(T2);
};
template <> void A<int>::f(int) { } // non-template member
template <> template <> void A<int>::f<>(int) { } // template member
int main
{
A<char> ac;
ac.f(1); //non-template
ac.f(’c’); //template
ac.f<>(1); //template
}
—end example]

Serpent555

Да, я тоже обнаружил, что возможность такой конструкции вообще не предусмотрена в стандарте С++ и является расширением Microsoft. Вынести такое объявление из тела класса нельзя. А жаль.
Тем не менее, вопрос так или иначе решен, большое всем спасибо! :cool:

naska79

Я бы попробовал заюзать include, типа такого
// in file foo_int_or_double.h
template<>
void foo<int_or_double_xxx>(int_or_double_xxx& x)
{
x--;
}
// in file foo.h
template<class T>
void foo(T& x)
{
x++;
}
#define int_or_double_xxx int
#include "foo_int_or_double.h"
#define int_or_double_xxx double
#include "foo_int_or_double.h"
Оставить комментарий
Имя или ник:
Комментарий: