Если я что-то упускаю, вам не нужен static_cast для создания объекта, тип времени выполнения которого является подклассом типа, который вы должны возвращать с фабрики, а затем использовать его полиморфно:
class Sub1 : public Super { ... };
class Sub2 : public Super { ... };
Super *factory(int param) {
if (param == 1) return new Sub1();
if (param == 2) return new Sub2();
return new Super();
}
int main(int argc, char **argv) {
Super *parser = factory(argc);
parser->parse(argv); // parse declared virtual in Super
delete parser;
return 0;
}
Паттерн, о котором говорит OP в вопросе, который вы упоминаете, заключается в том, чтобы получить Super * откуда-то, а затем привести его к типу времени выполнения, изучив RTTI и имея предложения if / else для всех подклассов, известных программисту. Это полная противоположность «Я использую объект полиморфно, как только он создан».
Теоретически, мой предпочтительный подход к десериализации - это цепочка ответственности: писать фабрики, способные просматривать сериализованные данные (включая заголовок типа) и самостоятельно решать, могут ли они построить объект из них. Для эффективности, пусть фабрики регистрируют, какие типы им интересны, чтобы они не все смотрели на каждый входящий объект, но я здесь это опускаю:
Super *deserialize(char *data, vector<Deserializer *> &factories>) {
for (int i = 0; i < factories.size(); ++i) { // or a for_each loop
Super *result = factories[i]->deserialize(data);
if (result != NULL) return result;
}
throw stop_wasting_my_time_with_garbage_data();
}
На практике я часто заканчиваю тем, что в любом случае пишу большой ключ, например, только с перечисляемым типом, некоторыми именованными константами и, возможно, виртуальным методом десериализации, вызываемым после конструирования:
Super *deserialize(char *data) {
uint32_t type = *((uint32_t *)data); // or use a stream
switch(type) {
case 0: return new Super(data+4);
case 1: return new Sub1(data+4);
case 2: return new Sub2(data+4);
default: throw stop_wasting_my_time_with_garbage_data();
}
}