Я хотел бы определить как класс X со статическим методом:
class X
{
static string get_type () {return "X";}
//other virtual methods
}
Я хотел бы вызвать классы, которые наследовались от X для переопределения get_type () метод и возвращаемые строки, отличающиеся от "X" (я счастлив, если они просто переопределяют get_type на данный момент).
Как я делаю это? Я знаю, что у меня не может быть виртуальных статических методов.
Править: Вопрос не о type_id, но в целом о статическом методе, который должен быть переопределен. Например,
class X {
static int getid() {return 1;}
}
Вот так
class X
{
static string get_type() {return "X"; }
};
class Y : public X
{
static string get_type() {return "Y"; }
};
Приведенный выше код делает именно то, что вы просили: производный класс переопределяет get_type
и возвращает другую строку. Если это не то, что вы хотите, вы должны объяснить, почему. Вы должны объяснить, что именно вы пытаетесь сделать и какого поведения вы ожидаете от этого статического метода. Если из вашего первоначального вопроса совершенно неясно.
Используйте Delphi, он поддерживает виртуальные статические члены в классах. ;>
template<int id>
class X {
public:
static int getid() { return id; }
};
class Y : public X<2> {
};
Вы не переопределили метод, но заставили каждый подкласс предоставлять идентификатор. Оговорка: я не пробовал, возможно, есть какая-то тонкая причина, по которой это не работает.
Вы не можете этого сделать по нескольким причинам. Вы не можете определить функцию в X и сделать ее чисто виртуальной. Вы вообще не можете иметь виртуальные статические функции.
Почему они должны быть статическими?
Я бы сказал, что вы знаете почему, но на всякий случай вот хорошее объяснение:
Похоже, вам придется найти выход из этого. Может быть, виртуальная функция, которая является оболочкой для синглтона?
Короче говоря, вы не можете этого сделать. Единственный способ потребовать от производного класса переопределить функцию базового класса - сделать ее чисто виртуальной (которая не может быть статической).
Вы упомянули несколько мест о гарантии того, что дочерние типы будут давать уникальные значения для вашей функции. Это, как говорили другие, невозможно во время компиляции [по крайней мере, без использования шаблонов, которые могут быть или не могут быть приемлемыми]. Но если вы отложите это до времени выполнения, вы, возможно, сможете сделать что-то подобное.
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 ненадежен в таких ситуациях, поэтому очевидно, что он также ненадежен.
Если я не ошибаюсь, чтобы вызвать статический метод, вы должны вызвать метод, указав точное имя класса, например.g X :: get_type ();
, DerivedClass :: get_type ()
и т. д. и в любом случае, если вызывается для объекта, динамический тип объекта не учитывается. . Так что, по крайней мере, в конкретном случае, он, вероятно, будет полезен только в шаблонном контексте, когда вы не ожидаете полиморфного поведения.
Однако я не понимаю, почему не должно быть возможности заставить каждый интересный класс (унаследованный или нет, поскольку «полиморфизм времени компиляции» не имеет значения) предоставлять эту функциональность с помощью шаблонов. В следующем случае вы должны специализировать функцию get_type
, иначе у вас будет ошибка времени компиляции:
#include <string>
struct X {};
struct Derived: X {};
template <class T> std::string get_type() {
static_assert(sizeof(T) == 0, "get_type not specialized for given type");
return std::string();
}
template <> std::string get_type<X>() {
return "X";
}
int main() {
get_type<X>();
get_type<Derived>(); //error
}
( static_assert
is C ++ 0x, в противном случае используйте вашу любимую реализацию , например, BOOST_STATIC_ASSERT
. И если вам не нравится специализация функций, вместо этого специализируйте структуру. И если вы хотите вызвать ошибку, если кто-то случайно попытается специализировать ее для типов, не производных от X, тогда это также должно быть возможно с type_traits
.)