2010-12-14 2 views
2

Использования частичной специализации шаблонов Я хотел бы создать функцию/метод:Частичная специализация шаблона для конкретного типа C++

A) обработка только один конкретный примитивный тип (целое, двойная, с плавающей точкой, ...) из формальный параметр и для других типов бросали исключением

template <class T> 
T min (Point <T> p) 
{ 
    /*if (T == int) continue; 
    else throw exception*/ 
} 

в) обработка более не-примитивные типы (определяемые пользователем типы) формального параметра и для других типов метательных исключение ...

Некоторые примеры кода будет быть полезным (без библиотеки C++ boost). Спасибо за вашу помощь.

+2

http://www.boost.org/doc/libs/1_45_0/libs/utility/enable_if.html – Anycorn

+5

Бросив исключения - это время выполнения, вывод аргумента шаблона - это время компиляции. Вы действительно хотите сделать это вместо, например. инициирование ошибки компилятора? –

+2

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

ответ

7

Это решение проблемы (часть A).

template<bool b> struct compile_time_assert; 

template<> 
struct compile_time_assert<true> 
{}; 

template<class T> 
struct is_int 
{ 
    static const bool value = false; 
}; 
template<> 
struct is_int<int> 
{ 
    static const bool value = true; 
}; 

template <class T> 
T min (Point <T> p) 
{ 
    /* 
    since you can check error at compile time, there is no reason to 
    raise exception (which is at runtime) if T is not int! 
    the following assert will not compile if T is not int. 
    not only that, you can even see the error message "_error_T_is_not_int" 
    if T is not int; 
    */ 

    compile_time_assert<is_int<T>::value> _error_T_is_not_int; 

    //your code 
} 

См. Этот образец кода.

  1. sample code1 когда T is int. Нет ошибки. Пожалуйста, проигнорируйте предупреждение !
  2. sample code2 если T - двойной. Теперь см. Также сообщение об ошибке . Пожалуйста, проигнорируйте предупреждение !

Аналогичным образом, вы можете написать шаблоны для других типов (double, char, whatever). Или, что еще лучше, вы можете просто объединить все это только в один struct и вместо этого можете определить много booleans (для каждого типа) в одном шаблоне, например static const bool is_T_int = true;. И т.д. Проведите эксперименты, вы узнаете!

Но тогда, интересно, хотите ли вы, чтобы ваша функция обрабатывала только один тип, тогда зачем определять шаблон для начала?


Для части (B) вы получаете идею из вышеперечисленного. Правильно? Делать эксперименты!

+1

http://www.boost.org/doc/libs/1_45_0/libs/type_traits/doc/html/index.html – Anycorn

+0

@lan: Я редактировал свой пост. Пожалуйста, проверьте это. Обратите внимание: нет исключения для повышения потребности, вы можете поймать проблему во время компиляции. Я просмотрел это с помощью двух примеров кода. :-) – Nawaz

+0

@Navaz: Спасибо ... Я думаю, что ошибка компилятора более подходит, чем ошибка бросания ... – Ian

1

вы можете использовать повышение :: MPL здесь часть B, но это использование наддува :(

#include <boost/mpl/at.hpp> 
#include <boost/mpl/map.hpp> 
#include <boost/mpl/bool.hpp> 

using namespace boost; 

typedef mpl::map< 
    mpl::pair<int, mpl::true_>, 
    mpl::pair<double, mpl::false_>, 
    mpl::pair<float, mpl::false_> 
> TAllowedTypesForMin; 

template <class T> 
T min (T p) 
{ 
    const bool allowed = mpl::at<TAllowedTypesForMin, T>::type::value; 
    if (allowed) 
    { 

    } 

    return p; 
} 

EDIT

со временем компиляции проверить все проще:

#include <boost/mpl/set.hpp> 
#include <boost/mpl/assert.hpp> 

using namespace boost; 

template<class T> struct Point {}; 
typedef mpl::set<int, double, float> TAllowedTypesForMin; 

template <class T> 
T min (Point<T> p) 
{ 
    typedef mpl::has_key<TAllowedTypesForMin, T> allowedType; 
    BOOST_MPL_ASSERT_MSG(allowedType::value, NOT_SUPPORTED_TYPE,()); 

// do something 

    return T(); 
} 

int main() 
{ 
     min(Point<long>()); 
     return 0; 
} 
1

Как уже упоминалось в комментарии Alexander C., вы уверены, что компилировать ошибку не подходит?

template <typename T> struct GateKeeper; 

// Func we want to call 
template <typename S, typename T> void foo (T t); 

// Warpper that checks the type and forwards the call 
template <typename T> inline void foo (T t) 
{ 
    // 
    // This call will fail for unless a specialization of 
    // GateKeeper for `T` is defined with a member TYPE 
    foo< typename GateKeeper<T>::TYPE, T > (t); 
} 

// 
// This declaration "allows" the type int. 
template <> struct GateKeeper<int> { typedef int TYPE; }; 

void bar() 
{ 
    foo (0); // Compiles 
    foo (0.0); // Causes error in wrapping foo 
} 

Частичная специализация может быть использован для разрешения любой специализации данного шаблона:

// Some template type 
template <typename T> 
class TmplType 
{ 
}; 

// This allows for specializations of TmplType 
template <typename T> struct GateKeeper< TmplType<T> > { typedef int TYPE; }; 

void bar() 
{ 
    TmplType<char> tt; 
    foo (tt); // Compiles 
} 

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

UPDATE: Что происходит:

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

Когда компилятор видит вызов foo(x), единственная функция, которую он может правильно выбрать, - foo<T>(T). Это связано с тем, что он не может выводить все параметры шаблона для foo<S,T>(T).

Тело foo<T>(T) направляет вызов к реальной функции:

foo< typename GateKeeper<T>::TYPE, T > (t); 

(ASIDE: см here для описания, когда использовать TypeName)

Это делает 2 вещи сразу.

Первый заключается в предоставлении двух параметров шаблона (S и T), которые необходимы для вызова другой функции.

Второй должен использовать элемент GateKeeper<T> как этот другой тип. Тип GateKeeper<T> должен быть полным и иметь этот элемент. Именно эта проверка, что позволяет определить, какие типы мы хотим разрешить и которые мы не делаем:

template <typename T> struct GateKeeper;     // Incomplete 
template <> struct GateKeeper<int> { typedef int TYPE; }; // Complete 

Как мы только при условии определения для GateKeeper<int> и не GateKeeper<double>, вызов foo(0) работает правильно, и foo(0.0) с ошибкой компиляции.

Чтобы double, нам просто нужно добавить явную специализацию для него:

template <> struct GateKeeper<double> { typedef int TYPE; }; // double now works. 
+0

Ошибка компиляции была бы более подходящей ... Я не понимаю эту строку: foo :: TYPE, T> (t); Там компилятор показывает ошибку. – Ian

+0

@Ian: Приведенный выше пример ожидает ошибки на этой строке в результате вызова «foo (0.0)». Если вы удалите его, он должен скомпилироваться. Я обновлю свой ответ немного подробнее о том, что происходит. –

+0

Мой комментарий о ошибке компилятора был связан с линией: foo :: TYPE, T> (t); – Ian