Грокаем C++
5.07K subscribers
6 photos
3 files
270 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам - @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
extern template с шаблонами STL

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

Оговорюсь, что буду говорить за реализацию стандартной библиотеки от gcc.

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

Далее. Единственным способом запретить неявное инстанцирование при распространении кода с помощью заголовочников - с помощью extern template. Все конкретизации мы не можем запретить, но самые популярные - можем попробовать. В случае успеха это будет давать большой импакт к уменьшению времени компиляции и размеру объектников.

А в недавнем посте "Не всегда инстанциация шаблона нужна для работы программы" мы поговорили о том, что методы, определенные внутри описания класса, неявно помечаются inline. А для inline функций запрет на неявное инстанцирование не работает!

Поэтому для шаблонов STL(под этим акронимом я понимаю все шаблонные классы стандартной библиотеки) вы просто не сможете получить никакого профита от extern template. Стандарт это явно запрещает.

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

Use your tools in proper places. Stay cool.

#cpcore #template #compiler
Так что лучше не мешайте статические и инлайновые сущности вместе. Опасный коктейль получается.

Sometimes mixing certain ingredients just won't work. Stay cool.

#cpp11 #cpcore
Константная инициализация. Ч2

То, каким образом в деталях работает константная иницализация, довольно сложно понять, просто читая стандарт и другие официальные источники. И вот почему.

Вот такой пример они дают:

struct S
{
static const int static_class_var;
};

static const int const_var = 10 * S::static_class_var;
const int S::static_class_var = 5;

int main()
{
std::cout << &const_var << std::endl; // ODR-use for explicit generation of symbol
std::array<int, S::static_class_var> a1; // OK
// std::array<int, const_var> a2; // ERROR
}


И говорится, что static_class_var на момент создания const_var не имеет инициализатора. А так как компилятор в первую очередь выполняет константную инициализацию, поэтому он не может сейчас установить значение для const_var. Поэтому в начале инициализируется static_class_var. И вот уже дальше - const_var.

Но видимо при проведении константной инициализации компилятор только один раз проходит сверху вниз программы. По факту, static_class_var - константа, инициализированная константным выражением. И по всем канонам должна сама стать константным выражением. Так и получается, ведь мы можем создать std::array из нее. Но вот из const_var - не можем. Хотя эта переменная тоже проинициализирована константным выражением. Но так, как ее инициализация происходит после константной инициализации, то этот факт не дает ей шанса стать нормальным constant expression.

Еще более интересные вещи происходят в ассемблер
е.

.section __TEXT,__const
.globl __ZN1S16static_class_varE ## @_ZN1S16static_class_varE
.p2align 2, 0x0
__ZN1S16static_class_varE:
.long 5 ## 0x5

.p2align 2, 0x0 ## @_ZL9const_var
__ZL9const_var:
.long 50 ## 0x32


Это внутреннее представление этих глобальных переменных. static_class_var прям глобальная, ее могу видеть и другие единицы трансляции. Поэтому она с пометкой .globl. const_var же статическая переменная, а значит ее видно видно только из текущей единицы трансляции.

Проблема в том, что const_var выглядит такой же compile-time константой, как и static_class_var. Хотя по идее тут должна быть какая-нибудь zero-инициализация + динамическая в рантайме. Но array мы не можем создать с const_var🗿.

Дело в том, что здесь замешано одно интересное право компилятора. Ему в определенных случаях разрешено устанавливать начальные значения переменным в compile-time, если он уверен, что их значения не изменится на момент начала старта программы.

Этот пример и пример из прошлого поста кстати показывают, что инициализация глобальных переменных идет не совсем сверху вниз. Для каждого этап инициализации это более менее верно, но под каждый этап попадают разные подмножества переменных. Поэтому могут возникать сайд-эффекты, подобные тем, что были в этом посте.

Accept side affects. Stay cool.

#cpcore #compiler