Как иметь статический счетчик для глобальных типов?

Мне нужен статический счетчик, который увеличивается каждый раз, когда я создаю другой тип, класс. Вот что я пробовал:

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, это просто то, что я сделал, чтобы заставить его работать, но должен быть более простой способ, верно?

Обновлено: на самом деле я удивлен, что решение не проще, все, что мне нужно, это для каждого класса, который я создаю в глобальном пространстве имен, получить новый идентификатор, я думаю, либо время компиляции, либо время выполнения.

Без понятия, но это проблема, которая должна быть связана с очень, очень аккуратным вариантом использования.

user4581301 23.12.2020 00:47

Вы хотите, чтобы это было известно во время компиляции?

Sergey Kolesnik 23.12.2020 00:52

Как насчет struct BlueHat : Hat, Component<BlueHat> {};?

Jarod42 23.12.2020 00:53

@ Jarod42 Jarod42 Разве это не двойное наследование от Component?

Zebrafish 23.12.2020 00:54

Верно, но Component — это пустой класс (и может быть простым трейтом вместо BTW или переменной шаблона).

Jarod42 23.12.2020 00:56

@Zebrafish "думал, что это структура CRTP Hat : Component<Hat> {};" Это. Я не знаю, почему πάντα ῥεῖ думает, что это не так.

Asteroids With Wings 23.12.2020 00:56

@Asteroids @Zebra Извините, вы правы. На самом деле это CRTP.. Но он не выводит что-то вроде T::static_ounter.

πάντα ῥεῖ 23.12.2020 00:59

@ πάνταῥεῖ То, что вы помещаете в базовый класс CRTP, ортогонально самому шаблону. Так же, как ваш выбор типов данных-членов не определяется ООП.

Asteroids With Wings 23.12.2020 01:08

Вас может заинтересовать время компиляции-template-stdintegral-constant-counter-how-to-im‌​plement-it

Jarod42 23.12.2020 01:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
93
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не вижу простого способа расширить ваше текущее решение, чтобы сделать это, если только вы не хотите начать добавлять virtual к каждому декларатору наследования и не забывать делать это каждый раз.

Во всяком случае, это похоже на анти-шаблон для «подсчета типов». У нас уже есть уникальные идентификаторы типов в виде typeid.

Если вам действительно нужен int, у вас может быть некоторый класс менеджера, который берет std::type_info и дает вам int, уникальный для этого типа, возможно, используя карту для его питания. Но если вы можете просто хранить std::type_info вместо этого, тем лучше. Недостатком является то, что эта информация не будет доступна статически (т.е. "во время компиляции").

Не так хорошо, как решение Джарода, но действительно ли это неправильно? Некоторые допустимые альтернативные подходы (виртуальное наследование решает исходную проблему, а type_info — правильный способ идентификации типов). Понижение кажется слишком большим.

Asteroids With Wings 23.12.2020 01:12
Ответ принят как подходящий

Вам не нужно наследование для (времени выполнения) счетчиков типов.

Вы даже можете использовать переменную шаблона (С++ 14):

std::size_t getId()
{
    static std::size_t counter = 0;
    return counter++;
}

template <typename T>
std::size_t Id = getId();

Демо .

Другие вопросы по теме