Мне нужен статический счетчик, который увеличивается каждый раз, когда я создаю другой тип, класс. Вот что я пробовал:
template <typename Type>
class Sequential_ID_Dispenser
{public:
static inline int nextDispensed = 0;
static int getID() { return nextDispensed++; }
};
struct DummyTypeForComponentIDs {}; // So that the same type is passed to getID()
template <typename T>
struct Component {
static inline int componentTypeID = Sequential_ID_Dispenser<DummyTypeForComponentIDs>::getID();
};
// The reason I've made it inherit like this is so that any time I add a new Component type/struct it'll automatically get an ID for that type
struct Hat : Component<Hat> {};
struct Tie : Component<Tie> {};
int main()
{
int id = Hat::componentTypeID; // = 0
id = Tie::componentTypeID; // = 1
}
Это работает. Но я хочу иметь возможность легко наследоваться от любого другого компонента, а это не работает, например:
template <typename T>
struct Component {
static inline int componentTypeID = Sequential_ID_Dispenser<DummyTypeForComponentIDs>::getID();
};
struct Hat : Component<Hat> {};
struct Tie : Component<Tie> {};
struct BlueHat : Hat {};
int main()
{
int id = Hat::componentTypeID; // = 0
id = Tie::componentTypeID; // = 1
id = BlueHat::componentTypeID; // = 0, gets the same number as struct Hat : Component<Hat>{}
}
Есть ли хорошее решение для этого? В идеале я хотел бы просто определить любую новую структуру, не передавая аргументы базовому конструктору. Я понимаю, что для этого у меня есть CRTP, это просто то, что я сделал, чтобы заставить его работать, но должен быть более простой способ, верно?
Обновлено: на самом деле я удивлен, что решение не проще, все, что мне нужно, это для каждого класса, который я создаю в глобальном пространстве имен, получить новый идентификатор, я думаю, либо время компиляции, либо время выполнения.
Вы хотите, чтобы это было известно во время компиляции?
Как насчет struct BlueHat : Hat, Component<BlueHat> {};
?
@ Jarod42 Jarod42 Разве это не двойное наследование от Component?
Верно, но Component
— это пустой класс (и может быть простым трейтом вместо BTW или переменной шаблона).
@Zebrafish "думал, что это структура CRTP Hat : Component<Hat> {};" Это. Я не знаю, почему πάντα ῥεῖ думает, что это не так.
@Asteroids @Zebra Извините, вы правы. На самом деле это CRTP.. Но он не выводит что-то вроде T::static_ounter
.
@ πάνταῥεῖ То, что вы помещаете в базовый класс CRTP, ортогонально самому шаблону. Так же, как ваш выбор типов данных-членов не определяется ООП.
Вас может заинтересовать время компиляции-template-stdintegral-constant-counter-how-to-implement-it
Я не вижу простого способа расширить ваше текущее решение, чтобы сделать это, если только вы не хотите начать добавлять virtual
к каждому декларатору наследования и не забывать делать это каждый раз.
Во всяком случае, это похоже на анти-шаблон для «подсчета типов». У нас уже есть уникальные идентификаторы типов в виде typeid.
Если вам действительно нужен int
, у вас может быть некоторый класс менеджера, который берет std::type_info
и дает вам int
, уникальный для этого типа, возможно, используя карту для его питания. Но если вы можете просто хранить std::type_info
вместо этого, тем лучше. Недостатком является то, что эта информация не будет доступна статически (т.е. "во время компиляции").
Не так хорошо, как решение Джарода, но действительно ли это неправильно? Некоторые допустимые альтернативные подходы (виртуальное наследование решает исходную проблему, а type_info
— правильный способ идентификации типов). Понижение кажется слишком большим.
Вам не нужно наследование для (времени выполнения) счетчиков типов.
Вы даже можете использовать переменную шаблона (С++ 14):
std::size_t getId()
{
static std::size_t counter = 0;
return counter++;
}
template <typename T>
std::size_t Id = getId();
Демо .
Без понятия, но это проблема, которая должна быть связана с очень, очень аккуратным вариантом использования.