Показанный код полон поддерживающего клея для переносимости и других синтаксисов c, которые слегка затеняют его основную реализацию. Проще понять основную концепцию происходящего здесь, рассмотрев гораздо более упрощенный пример:
#include <iostream>
class family {
inline static int identifier=0;
public:
template <typename... Type>
inline static const int type = identifier++;
};
int main()
{
std::cout << "int: " << family::type<int> << std::endl;
std::cout << "const char *: "
<< family::type<const char *> << std::endl;
std::cout << "int again: " << family::type<int> << std::endl;
return 0;
}
g ++ 9.2.1, с -std=c++17
выдает следующий вывод:
int: 0
const char *: 1
int again: 0
family
инициализируется с помощью элемента identifier
, по умолчанию инициализированного равным 0.
Основная концепция ядра C ++ здесь заключается в том, что шаблон создается при первом обращении к нему. При первом обращении к type<int>
он создается, и инициализируется по умолчанию из выражения identifier++
, которое инициализирует этот экземпляр type
и увеличивает значение identifier
. Каждый новый экземпляр type
инициализируется одинаково, снова увеличивая identifier
. использование ранее использованного type
просто использует уже созданный экземпляр шаблона с его первоначально инициализированным значением.
Это базовая концепция c, используемая здесь. Остальная часть показанного кода представляет собой несколько видов оформления витрин, то есть, используя std::atomic
, если доступно, и выбирая лучший тип для счетчика.
Обратите внимание, что этот трюк полон минных полей, когда задействованы несколько единиц перевода , Вышеуказанный подход работает без каких-либо неожиданных сюрпризов, когда он используется только в одной единице перевода. Эти шаблоны, похоже, содержат некоторые положения для использования нескольких единиц перевода, но с независимым счетчиком для каждой единицы перевода. Это еще одно осложнение, которое скрывает показанный код ...