Это дополнение к отличному ответу @Kerrek SB. Я бы добавил его в качестве комментария, но их уже много, поэтому новые комментарии по умолчанию скрыты.
Итак, его и другие примеры, которые я видел, «просты» в том смысле, что тип статической переменной-члена известен заранее. Это легко, потому что компилятор, например, знает размер хранилища для любого экземпляра шаблона, поэтому можно подумать, что компилятор может использовать фанк-схему искажения, определить выходную переменную один раз и разгрузить остальное компоновщику, и это может даже сработать.
Но немного удивительно, что он работает, когда тип статического члена зависит от параметра шаблона. Например, следующие работы:
template <typename width = uint32_t>
class Ticks : public ITimer< width, Ticks<width> >
{
protected:
volatile static width ticks;
}
template <typename width> volatile width Ticks<width>::ticks;
(Обратите внимание, что явное создание статических переменных не требует (или допускает) спецификации по умолчанию для "ширины").
Таким образом, возникает больше мыслей о том, что компилятор C ++ должен выполнить довольно большую обработку - в частности, для создания экземпляра шаблона требуется не только сам шаблон, но он также должен собирать все явные экземпляры [static member] (тогда можно только удивляться, почему они были сделаны отдельными синтаксическими конструкциями, а не чем-то, что должно быть прописано в классе шаблона).
Что касается реализации этого на уровне компоновщика, для GNU binutils его "общие символы":
http://sourceware.org/binutils/docs/as/Comm.html#Comm. (Для инструментальных цепочек Microsoft он называется COMDAT, как говорится в другом ответе).