Ответ один
Общий метод получения такого класса - Любопытно повторяющийся шаблон (CRTP) :
class PingMessage: public MessageTmpl < 10, PingMessage >
Ваш особый метод использования статической инициализации члена класса шаблона для регистрации подклассов этого класса (IMO) просто великолепен, и я никогда раньше такого не видел. Более распространенный подход, используемый средами модульного тестирования, такими как UnitTest ++ и Google Test , заключается в предоставлении макросов, объявляющих как класс, так и отдельную статическую переменную, инициализирующую этот класс.
Ответ два
Статические переменные инициализируются в указанном порядке. Если вы переместите объявление m_List до вызовов MessageFactory :: Register, вы должны быть в безопасности. Также имейте в виду, что если вы начнете объявлять подклассы Message более чем в одном файле, вам придется обернуть m_List как синглтон и проверять его инициализацию перед каждым использованием, из-за статического порядка инициализации C ++ fiasco .
Ответ три
Компиляторы C ++ будут создавать экземпляры только тех элементов шаблона, которые действительно используются. Статические члены шаблонных классов - это не та область C ++, которую я часто использовал, поэтому я могу ошибаться, но похоже, что предоставления конструктора достаточно, чтобы заставить компилятор думать, что используется MESSAGE_ID (таким образом гарантируя, что MessageFactory :: Регистр называется).
Это кажется мне не очень понятным, так что это может быть ошибка компилятора. (Я тестировал это в g ++ 4.3.2; мне любопытно узнать, как, например, Comeau C ++ справляется с этим.)
Достаточно явно создать экземпляр MESSAGE_ID, по крайней мере, в g ++ 4.3.2:
template const uint16_t PingMessage::MESSAGE_ID;
Но это даже более ненужная работа, чем предоставление пустого конструктора по умолчанию.
Я не могу придумать хорошего решения, использующего ваш текущий подход; Лично у меня было бы желание переключиться на методику (такую как макросы или использование скрипта для генерации части ваших исходных файлов), которая меньше полагалась на продвинутый C ++. (У сценария было бы дополнительное преимущество, заключающееся в упрощении обслуживания MESSAGE_ID.)
В ответ на ваши комментарии:
Синглетоны обычно , которых следует избегать, потому что они часто используются как плохо замаскированные глобальные переменные. Однако в некоторых случаях вам действительно нужна глобальная переменная, и одним из таких случаев является глобальный реестр доступных подклассов сообщений.
Да, предоставленный вами код инициализирует MESSAGE_ID, но я говорил о явном создании экземпляра каждого подкласса MESSAGE_ID. Явное создание экземпляра относится к указанию компилятору создавать экземпляр шаблона, даже если он считает, что этот экземпляр шаблона не будет использован в противном случае.
Я подозреваю, что статическая функция с энергозависимым присваиванием предназначена для того, чтобы обмануть или заставить компилятор сгенерировать присваивание MESSAGE_ID (чтобы обойти проблемы, которые я и dash-tom-bang указали при сбрасывании компилятора или компоновщика или создание экземпляра).