Специализация шаблона для нескольких типов

Название немного двусмысленное.

Допустим, у меня есть шаблон, определенный как:

template < typename T >
void foo ( int x ) ;
template <>
void foo<char> ( int x ) ;
template <>
void foo<unsigned char> ( int x ) ;
template <>
void foo<short> ( int x ) ;
...

Внутренне и foo<signed>(), и foo<unsigned>() делают одно и то же. Единственное требование состоит в том, чтобы T был 8-битным типом.

Я мог бы сделать это, создав еще один шаблон для определения стандартного типа на основе размера.

template < typename T, size_t N = sizeof( T ) > struct remap ;
template < typename T, size_t > struct remap< 1 >
{
    typedef unsigned char value;
}
...

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

Каков самый элегантный способ решить эту проблему без повторения этих объявлений функций?

Это не вопрос С++ 11.


person Twifty    schedule 13.07.2013    source источник
comment
Вы пытались использовать enable_if для проверки размера типа? Я предполагаю, что вы делаете то же самое для всех встроенных типов одного размера?   -  person Borgleader    schedule 13.07.2013
comment
@Borgleader enable_if — это С++ 11   -  person Twifty    schedule 13.07.2013
comment
SFINAE и enable_if также могут быть реализованы на C++03.   -  person dyp    schedule 13.07.2013
comment
@DyP У меня нет доступа к полной версии C++11, поэтому я никогда не использовал enable_if. Как бы решить эту проблему?   -  person Twifty    schedule 13.07.2013
comment
Если я правильно вас понял, вы хотите избежать дублирования кода. Самый простой способ, который я вижу, - это вызвать одну и ту же функцию изнутри foo<signed> и foo<unsigned>. Есть и другие способы, похожие на ваше remap предложение, но я думаю, что вы получите больше кода, чтобы избежать дублирования.   -  person dyp    schedule 13.07.2013
comment
Я думаю, вы имеете в виду foo<signed char> и foo<unsigned char>, верно? signed и unsigned - это ints, и они имеют ширину не менее 16 бит. Также обратите внимание, что char, signed char и unsigned char — это три разных типа.   -  person dyp    schedule 13.07.2013


Ответы (2)


Вам нужна ваша черта remap, чтобы просто отображать типы ввода в типы вывода, и делегировать функцию интерфейса foo<T>(int) в реализацию foo_implementation<remap<T>::type>(int). то есть:

template <typename T>
struct remap {
    // Default: Output type is the same as input type.
    typedef T type;
};

template <>
struct remap<char> {
    typedef unsigned char type;
};

template <>
struct remap<signed char> {
    typedef unsigned char type;
};

template <typename T>
void foo_impl(int x);

template <>
void foo_impl<unsigned char>(int x) {
    std::cout << "foo_impl<unsigned char>(" << x << ") called\n";
}

template <typename T>
void foo(int x) {
    foo_impl<typename remap<T>::type>(x);
}

Посмотрите на сайте ideone.com.

Тем не менее, может быть проще определить foo_char, foo_int и foo_short и просто вызвать правильный из клиентского кода. foo<X>() синтаксически не сильно отличается от foo_X().

person Casey    schedule 13.07.2013

Одна из возможностей состоит в том, чтобы специализировать шаблон класса для нескольких типов одновременно:

// from: http://en.cppreference.com/w/cpp/types/enable_if
    template<bool B, class T = void>
    struct enable_if {};

    template<class T>
    struct enable_if<true, T> { typedef T type; };

template < typename A, typename B >
struct is_same
{
    static const bool value = false;
};
template < typename A >
struct is_same<A, A>
{
    static const bool value = true;
};


template < typename T, typename dummy = T >
struct remap;

template < typename T >
struct remap
<
    T,
    typename enable_if<    is_same<T, unsigned char>::value
                        || is_same<T, signed char>::value, T >::type
>
{
    void foo(int);
};


int main()
{
    remap<signed char> s;
    s.foo(42);
}

Другая возможность состоит в том, чтобы специализировать шаблон класса для категорий типов (признаков типа):

#include <cstddef>

template < typename T >
struct is_integer
{
    static const bool value = false;
};
template<> struct is_integer<signed char> { static const bool value = true; };
template<> struct is_integer<unsigned char> { static const bool value = true; };


template < typename T, typename dummy = T, std::size_t S = sizeof(T) >
struct remap;

template < typename T >
struct remap
<
    T
    , typename enable_if<is_integer<T>::value, T>::type
    , 1 // assuming your byte has 8 bits
>
{
    void foo(int);
};


int main()
{
    remap<signed char> s;
    s.foo(42);
}
person dyp    schedule 13.07.2013
comment
Я просто хотел отметить, что есть std::enable_if, поэтому вам не нужно определять его самостоятельно. См. здесь. - person leetNightshade; 27.03.2015
comment
@leetNightshade enable_if добавлен в C++11; вопрос нацелен на С++ 03 (также я уже добавил комментарий к OP с той же ссылкой;) - person dyp; 27.03.2015