Определение статических полей класса



Если вы хоть раз пытались наивно инициализировать статический член класса внутри самого класса, то явно знаете, о чем речь пойдет. А для тех, кто не знает, скажу, что произойдет дальше. Компилятор выдаст ошибку типа: ISO C++ forbids in-class initialization of non-const static member. Стандарт запрещает неконстантным статическим полям инициализироваться внутри класса. Ранее стандартной практикой для решения этой проблемы был вынос определения этого поля в цпп файл. Решение довольно неудобное, ибо каждый раз нужно лезть в файл реализации, чтобы посмотреть инициализатор. Да и писать это не очень удобно. В потенциале для такой инициализации нужно дополнительно написать в 1.5 раза больше букав, чем при удобной in-class инициализации. Если это так неудобно, то почему такое правило вообще введено?



Давайте небольшой рекап для статических членов. Статическое поле класса - по сути глобальный объект, который как бы присоединен к классу. Любой объект класса может получить доступ к одному и тому же инстансу статической переменной. Как и любой код, который может создать объект класса, сможет использовать его статический член через имя класса. Типа такого ClassType::static_field. То есть, несмотря на то, что поле объявлено как static, оно имеет внешнее связывание. То есть существует лишь один инстанс этого поля, который виден всему коду, имеющему доступ к классу. И для такой сущности применяется One Definition Rule(ODR), которое говорит, что у переменной или функции(за некоторыми исключениями) внутри ВСЕЙ программы может быть сколько угодно объявлений, но только одно определение. Ща поясню, к чему это приводит.



Вот у вас есть описание класса в каком-то хэдере. Как использовать этот класс? Заинклюдить этот хэдер в нужный файлик. А что делает иклюд? Правильно, на этапе препроцессора он просто заменяется на текст подключаемого файла. Поэтому если у вас определение статического поля находится в хэдере, значит у вас есть определение этого поля во всех единицах трансляции, куда попало описание класса. А это прямое нарушение ODR.



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



Как меняет ситуацию определение поля вне класса в цпп файле? Это позволяет не нарушать ODR, все просто) Ну если чуть подробнее, то во всех единицах трансляции, куда мы подключили описание класса, будет только объявление нашего поля. И только одно определение будет браться из нашего цпп файлика.



И кстати в файле реализации нельзя приписывать определению переменной пометку static. Это путает компилятор. Обычно для переменных static значит статическое время жизни и внутреннюю линковку, а нам здесь нужна внешняя линковка.



Это кстати довольно важно подсветить: static для обычных глобальных переменных меняет их тип линковки на внутреннюю, а внутри класса static обозначает внешнюю линковку(что с методами, что с полями).



Но начиная с 17-х плюсов есть намного более изящный способ обойти ODR в этом конкретном случае. Постоянные читатели или просто знающие люди уже понимают, о чем речь. Но пост об этом будет позже)



Define yourself. Stay cool.



#cppcore