Принудительный тип шаблона C ++ - PullRequest
12 голосов
/ 16 марта 2010

У меня есть базовый шаблонный класс, но я бы хотел ограничить тип специализации набором классов или типов. e.g.:

template <typename T>
class MyClass
{
.../...
private:
    T* _p;
};

MyClass<std::string> a; // OK
MYCLass<short> b;       // OK
MyClass<double> c;      // not OK

Это только примеры, допустимые типы могут отличаться.

Это вообще возможно? Если да, то как это сделать?

Спасибо.

Ответы [ 6 ]

17 голосов
/ 17 марта 2010

Другая версия - оставить ее неопределенной для запрещенных типов

template<typename T>
struct Allowed; // undefined for bad types!

template<> struct Allowed<std::string> { };
template<> struct Allowed<short> { };

template<typename T>
struct MyClass : private Allowed<T> { 
  // ...
};

MyClass<double> m; // nono
8 голосов
/ 16 марта 2010

Просто быстрая идея, я уверен, что есть лучшие подходы:

template <typename T> struct protector {
static const int result = 1;
};

template <> struct protector<double> {
static const int result = -1;
};

template <typename T> 
class MyClass
{
   private:
     char isfine[protector<T>::result];
};

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

6 голосов
/ 16 марта 2010

Взгляните на библиотеку Boost Concept Check: http://www.boost.org/doc/libs/1_42_0/libs/concept_check/concept_check.htm

2 голосов
/ 16 марта 2010

Как правило, нет необходимости ограничивать типы шаблонов, с которыми можно создавать экземпляры. Либо шаблон скомпилируется с данным типом (и работает нормально), либо нет (и выдает ошибку компилятора без каких-либо усилий со стороны программиста).


Если вам нужно наложить ограничения, обычно у типов есть что-то общее, что может быть описано некоторыми уже доступными чертами типов (стандартная библиотека, boost::type_traits), или вы можете создать для них новую черту типа.

Например, вот шаблонный класс, который позволяет только целочисленные типы, используя std::numeric_limits, чтобы проверить его (если вы пишете свой собственный числовой тип, вы можете специализировать его, чтобы он также работал с вашим новым целочисленным типом). static_assert - это только C ++ 0x, если недоступно, используйте BOOST_STATIC_ASSERT или другой трюк.

#include <limits>
#include <string>

template <class T>
class X
{
    static_assert(std::numeric_limits<T>::is_integer, "X can be only instantiated with integer types");
    //...
};

int main()
{
    X<int> xi;
    X<char> xc;
    //X<double> xd;
    //X<std::string> xs;
}

Если вы планируете поддерживать только несколько произвольных типов, не имеющих ничего общего (как видно из вашего гипотетического примера), одним из способов является использование списков типов. Опять же, повышение может сделать задачу намного проще, но вот как вы можете свернуть свое собственное (это только наполовину, потребуется дополнительная работа, чтобы сделать объявление в списке типов красивее).

struct no_type {};

template <class T, class U = no_type>
struct t_list
{
    typedef T head;
    typedef U tail;
};

//trait to check if two types are identical
template <class T, class U>
struct is_same
{
    static const bool value = false;
};

template <class T>
struct is_same<T, T>
{
    static const bool value = true;
};

//compile-time recursion to check if T matches any type in list L
template <class T, class L>
struct in_type_list
{
    static const bool value =
        is_same<T, typename L::head>::value || in_type_list<T, typename L::tail>::value;
};

//terminates recursion
template <class T>
struct in_type_list<T, no_type>
{
    static const bool value = false;
};

template <class T>
class X
{
    typedef t_list<double, t_list<int, t_list<char> > > allowed_types; //double, int, char

    //poor man's static_assert
    typedef int check_type [in_type_list<T, allowed_types>::value ? 1 : -1];
    //...
};

int main()
{
    X<char> xc;
    X<int> xi;
    X<double> xd;
    //X<float> xf;
}
1 голос
/ 16 марта 2010

Я не уверен в этом, но вы могли бы добавить другую специализацию шаблона для двойного шаблон

class MyClass
{
.../...
private:
    T* _p;
};

template <double> class MyClass
{};

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

В общем, я бы добавил утверждение компиляции для проверки на нежелательные типы.

Надеюсь, это поможет.

1 голос
/ 16 марта 2010

Существуют различные приемы, позволяющие проверять некоторые вещи, в зависимости от того, разрешены или нет ваши критерии для создания экземпляров. На практике вы должны использовать библиотеку с более высоким рычагом для таких как Boost's Concept Check.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...