Ограничения шаблона C ++ - PullRequest
       11

Ограничения шаблона C ++

57 голосов
/ 23 сентября 2008

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

interface IFoo
{
}


class Foo<T> where T : IFoo
{
}

class Bar : IFoo
{
}

class Simpson
{
}

class Program
{
    static void Main(string[] args)
    {
        Foo<Bar> a = new Foo<Bar>();
        Foo<Simpson> b = new Foo<Simpson>(); // error CS0309
    }
}

Есть ли способ, которым мы можем наложить ограничения для параметров шаблона в C ++.


C ++ 0x имеет встроенную поддержку для этого, но я говорю о текущем стандарте C ++.

Ответы [ 9 ]

50 голосов
/ 08 апреля 2014

Если вы используете C ++ 11, вы можете использовать static_assert с std::is_base_of для этой цели.

Например,

#include <type_traits>

template<typename T>
class YourClass {

    YourClass() {
        // Compile-time check
        static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");

        // ...
    }
}
34 голосов
/ 23 сентября 2008

«Неявно» - правильный ответ. Шаблоны эффективно создают сценарий «типизирования утки» благодаря способу их компиляции. Вы можете вызывать любые функции по значению типа шаблона, и единственные экземпляры, которые будут приняты, это те, для которых этот метод определен. Например:

template <class T>
int compute_length(T *value)
{
    return value->length();
}

Мы можем вызвать этот метод для указателя на любой тип, который объявляет метод length() для возврата int. Thusly:

string s = "test";
vector<int> vec;
int i = 0;

compute_length(&s);
compute_length(&vec);

... но не указатель на тип, который не объявляет length():

compute_length(&i);

Этот третий пример не скомпилируется.

Это работает, потому что C ++ компилирует новую версию шаблонизированной функции (или класса) для каждого экземпляра. Выполняя эту компиляцию, он выполняет прямую, почти макроподобную подстановку экземпляра шаблона в код перед проверкой типа. Если все работает с этим шаблоном, компиляция продолжается, и мы в итоге получаем результат. Если что-то не получается (например, int* не объявляет length()), то мы получаем страшную ошибку во время компиляции шаблона из шести страниц.

33 голосов
/ 23 сентября 2008

Как уже упоминал кто-то другой, C ++ 0x встроен в язык. До этого я бы рекомендовал Bjarne Stroustrup предложения для ограничений шаблона .

Редактировать: Повышение также имеет собственную альтернативу .

Edit2: похоже, концепции были удалены из C ++ 0x .

14 голосов
/ 23 сентября 2008

Вы можете установить тип защиты на IFoo, который ничего не делает, убедитесь, что он есть на T в Foo:

class IFoo
{
public:
    typedef int IsDerivedFromIFoo;
};

template <typename T>
class Foo<T>
{
    typedef typename T::IsDerivedFromIFoo IFooGuard;
}
8 голосов
/ 23 сентября 2008

Выезд Повышение

Библиотека проверки концепции Boost (BCCL)

Библиотека Concept Check позволяет добавить явное утверждение и проверку концепций в стиле предлагаемого расширения языка C ++ .

2 голосов
/ 23 сентября 2008

Вроде. Если вы используете static_cast для IFoo *, то создание шаблона будет невозможно, если только вызывающая сторона не передаст класс, который может быть присвоен IFoo *.

1 голос
/ 23 сентября 2008

Только неявно.
Любой метод, который вы используете в методе, который фактически вызывается, накладывается на параметр шаблона.

0 голосов
/ 04 декабря 2008

Посмотрите на шаблон CRTP (Любопытно рекурсивный шаблон). Он предназначен для поддержки статического наследования.

0 голосов
/ 30 сентября 2008

Вы можете сделать это. Создайте базовый шаблон. Сделать это могут только частные конструкторы. Затем создайте специализации для каждого случая, который вы хотите разрешить (или сделайте обратное, если запрещенный список намного меньше разрешенного списка).

Компилятор не позволит вам создавать экземпляры шаблонов, которые используют версию с закрытыми конструкторами.

В этом примере разрешено создание экземпляров только с int и float.

template<class t> class FOO { private: FOO(){}};

template<> class FOO<int>{public: FOO(){}};

template<> class FOO<float>{public: FOO(){}};

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

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