Вы упомянули несколько мест о гарантии того, что дочерние типы дают уникальные значения для вашей функции. Это, как уже говорили другие, невозможно во время компиляции [по крайней мере, без использования шаблонов, которые могут или не могут быть приемлемы]. Но если вы откладываете это до времени выполнения, вы можете извлечь что-то подобное.
class Base {
static std::vector<std::pair<const std::type_info*, int> > datas;
typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
public:
virtual ~Base() { }
int Data() const {
const std::type_info& info = typeid(*this);
for(iterator i = datas.begin(); i != datas.end(); ++i)
if(*(i->first) == info) return i->second;
throw "Unregistered Type";
}
static bool RegisterClass(const Base& p, int data) {
const std::type_info& info = typeid(p);
for(iterator i = datas.begin(); i != datas.end(); ++i) {
if(i->second == data) {
if(*(i->first) != info) throw "Duplicate Data";
return true;
}
if(*(i->first) == info) throw "Reregistering";
}
datas.push_back(std::make_pair(&info, data));
return true;
}
};
std::vector<std::pair<const std::type_info*, int> > Base::datas;
class Derived : public Base { };
const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);
class OtherDerived : public Base { };
const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception
Предостережения: это полностью не проверено. Исключения будут выданы перед вводом main, если вы сделаете это таким образом. Вы можете перенести регистрацию в конструкторы и принять на себя накладные расходы на проверку регистрации, если хотите.
Я выбрал неупорядоченный вектор для простоты; Я не уверен, что type_info::before
обеспечивает необходимую семантику, которая будет использоваться в качестве предиката для карты, и, вероятно, у вас не будет столько производных классов, что линейный поиск будет проблематичным в любом случае. Я храню указатель, потому что вы не можете скопировать type_info
объекты напрямую. Это в основном безопасно, поскольку время жизни объекта, возвращаемого typeid
, составляет всю программу. Могут быть проблемы, когда программа закрывается, я не уверен.
Я не пытался защитить от статического порядка ошибок инициализации. Как написано, в какой-то момент это не получится.
Наконец, нет, это не статично, но «статический» и «виртуальный» в любом случае не имеют смысла вместе. Если у вас нет экземпляра типа для действия, то как вы узнаете, какой перезаписанный метод выбрать? Есть несколько случаев с шаблонами, когда вы могли бы на законных основаниях хотеть вызвать статический метод без реального объекта, но это вряд ли будет распространено.
* edit: Кроме того, я не уверен, как это взаимодействует с динамически связанными библиотеками и тому подобным. Я подозреваю, что RTTI ненадежен в таких ситуациях, поэтому, очевидно, это так же ненадежно.