Мой вопрос, который находится в последнем абзаце, нуждается (на мой взгляд) в некоторой пояснительной настройке.По сути, мне интересно, можете ли вы избежать использования шаблонов, если вместо этого вы сделаете все свои потенциальные классы шаблонов вместо того, чтобы наследовать от базового класса, который объявляет виртуальные методы, которые вы будете использовать, среди них функция для выделения памяти, которая при реализациивернет указатель на производный (не базовый) тип.
НАЧАЛО настройки
C ++, похоже, не имеет понятия "универсального базового класса", из которого все неявно является производным;Я полагаю, что класс будет определен следующим образом:
class universal_base
{
};
Конечно, теперь, когда я определил его, я могу сделать все мои классы производными от него.Тогда из-за полиморфизма любые ссылки или указатели на universal_base
, которые я передаю, будут в основном такими же, как параметры шаблона:
template <typename T>
class C
{
T &x;
int f(T &y);
C(T &z): x(z + 1) {}
};
class C
{
universal_base &x;
int f(universal_base &y);
C(universal_base &z): x(z + 1) {}
};
Разница в том, что в первой конструкции выражение z + 1
не может быть гарантированно действительным;Вы просто должны сказать пользователям, что T
должен перегрузить operator+
.Во второй конструкции я мог бы добавить виртуальный такой оператор к universal_base
:
// in universal_base
public:
virtual universal_base& operator+(const universal_base &x) = 0;
и использовать typeid
и dynamic_cast
в реализациях, чтобы получить правильный аргумент.Таким образом, невозможно написать плохо сформированный код, потому что компилятор будет жаловаться, если вы не реализуете operator+
.
Конечно, таким способом невозможно объявить элементы не ссылкамиТип:
class C: public universal_base
{
universal_base x; // Error: universal_base is a virtual type
};
Однако, это можно обойти путем тщательного использования инициализации.На самом деле, если бы я хотел создать шаблон для вышеприведенного,
template <typename T>
class C: public universal_base
{
T x;
};
, я бы почти наверняка дал ему объекты типа T
в какой-то момент.В этом случае нет причины, по которой я не смог бы сделать следующее:
class universal_base
{
public:
virtual universal_base& clone() = 0;
};
class C: public universal_base
{
universal_base &x;
C(universal_base &y) : x(y.clone()) {}
}
По сути, я создаю переменную типа, который определяется во время выполнения.Это, конечно, требует, чтобы каждый объект типа C
был должным образом инициализирован, но я не думаю, что это огромная жертва.
Это не академично, поскольку оно имеет следующее применение: если я пишуМодуль, который предназначен для связи с другими программами и обработки их данных каким-то общим способом, я не могу знать, какие типы будут использоваться.Шаблоны в этой ситуации бесполезны, но описанный выше метод работает нормально.
END setup
Итак, мой вопрос: полностью ли это заменяет шаблоны, по модулю что-то про инициализацию?Это неэффективно или опасно как-то?