Предположим, что у нас есть перечислимый тип:
enum DataType { INT, DOUBLE };
и картограф типа:
template<DataType T>
struct TypeTraits {};
template<>
struct TypeTraits<INT> { typedef int T; };
template<>
struct TypeTraits<DOUBLE> { typedef double T; };
И несколько шаблонов, представляющих операции (не беспокойтесь об уродливых указателях void и типах типа C):
struct Operation {
DataType rettype;
Operation(DataType rettype) : rettype(rettype);
virtual void* compute();
};
template<DataType RetType>
class Constant : public Operation {
typedef typename TypeTraits<RetType>::T RType;
RType val;
Constant(RType val) : val(val), Operation(RetType) {};
virtual void* compute(){ return &val; }
};
template<DataType T1, DataType T2, DataType RetType>
class Add : public Operation {
typedef typename TypeTraits<RetType>::T1 T1Type;
typedef typename TypeTraits<RetType>::T2 T2Type;
typedef typename TypeTraits<RetType>::RetType RType;
RType val;
Operation *c1, *c2;
Add(Operation *c1, Operation *c2) : c1(c1), c2(c2), Operation(RetType) {};
virtual void* compute(){
T1Type *a = (T1Type *)c1->compute();
T2Type *b = (T2Type *)c2->compute();
val = *a + *b;
return &val;
}
};
И представление абстрактного дерева:
class AbstractNode {
enum Type { ADD, INT_CONSTANT, DOUBLE_CONSTANT };
Type type;
int intval;
double doubleval;
child1 *AbstractNode;
child2 *AbstractNode;
}
Мы читаем сериализованное абстрактное дерево из входных данных, чтобы преобразовать его в дерево операций, а затем - вычислить результат.
Мы хотим написать что-то вроде:
algebrator(Operation *op){
if(op->type == AbstractNode::INT_CONSTANT)
return new Constant<INT>(op->intval);
else if(op->type == AbstractNode::DOUBLE_CONSTANT)
return new Constant<DOUBLE>(op->doubleval);
else {
Operation *c1 = algebrator(op->child1),
*c2 = algebrator(op->child2);
DataType rettype = add_types_resolver(c1->rettype, c2->rettype);
return new Add<c1->rettype, c2->rettype, rettype>(c1, c2);
}
}
, где add_types_resolver
- это что-то, что указывает тип возврата операции добавления на основе типов аргументов операции.
И мы терпим неудачу, конечно, и компилятор ударит нас в лица. Мы не можем использовать переменную в качестве переменной шаблона! Это потому, что вся информация, необходимая для создания экземпляра шаблона, должна быть доступна во время компиляции!
А теперь - вопрос.
Есть ли другое решение, кроме написания большого количества операторов if-else или switch-case? Разве мы не можем попросить компилятор раскрыть все случаи во время компиляции? Шаблон параметризован перечислением, поэтому у нас есть гарантия, что такой процесс конечен.
И, пожалуйста, не пишите ответы типа "Я думаю, что весь пример испорчен". Я просто хочу знать, есть ли способ заполнить шаблон переменной, зная, что он из конечного небольшого набора.
Все это может показаться излишним, но мне действительно любопытно, как я могу создавать экземпляры классов в таких необычных ситуациях.