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

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

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
    }
}

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


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

Ответы

Ответ 2

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

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);

Этот третий пример не будет компилироваться.

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

Ответ 3

Если вы используете С++ 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");

        // ...
    }
}

Ответ 4

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

class IFoo
{
public:
    typedef int IsDerivedFromIFoo;
};

template <typename T>
class Foo<T>
{
    typedef typename T::IsDerivedFromIFoo IFooGuard;
}

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

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

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

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

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

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

Ответ 9

Посмотрите на шаблон CRTP (Curiously Recursive Template Pattern). Он предназначен для поддержки статического наследования.