Линковочная природа шаблонов
Когда мы говорим про шаблоны и их линковку, нам важно видеть все детали общей картины. Иначе полное понимание так и не придет. Поэтому сегодня немного больше приоткроем линковочные тайны темплейтов.
Думаю, что не будет грубым обобщением сказать, что каждый из нас пользовался шаблонным классом std::vector. И это хорошо, все имеют опыт с ним и всем будет проще понимать, о чем сейчас пойдет речь.
Представим, что мы разрабатываем какое-то приложение или отдельный сервис. Этот сервис состоит из отдельных кодовых модулей, которые отдельно компилируются и линкуются вместе для получения готового бинарника. Так вот очень легко допустить, что во многих модулях одного и того же сервиса используется вектор интов. std::vector<int>. Обычно мы просто инклюдим в эти модули хэдэр вектора и после используем его. Таким образом происходит неявная инстанциация. То есть компилятор на этапе компиляции модулей сам инстанциацирует интовую специализацию вектора в каждой единице трансляции и использует ее в коде текущего юнита.
Однако, погодите-ка. То есть у нас в нескольких единицах трансляции есть одна и та же скомпилированная сущность и при линковке это не вызывает никаких проблем. Как так?
Вот, что стандарт говорит по поводу этого:
Стандарт разрешает шаблонным сущностям иметь больше, чем одно определение на всю программу. И не более одного на каждую единицу трансляции.
И кстати, утверждение, что шаблоны неявно помечены inline - неверно. Но об этом позже.
За счет чего допускается возможность наличия нескольких определений сущности в программе? Если вы читали гайд по inline(можете найти в закрепе), то, наверняка, знаете ответ. За счет слабых символов.
Скомпилируем гццшкой самую простенькую функцию:
А утилитка nm покажет нам природу символов в бинаре. Там будет генерироваться оч много функций и символов связанных с вектором, поэтому разберем только один пример с конструктором, символ которого выглядит так:
0000000000000000 W std::vector<int, std::allocator<int> >::vector(unsigned long, std::allocator<int> const&)
Вот эта буковка W говорит, что этот символ - слабый. А слабые символы могут быть перезаписаны во время линковки. Линковщик просто сам выберет одно понравившееся ему определение из всех существующих в программе и перезапишет им остальные. Таким образом в программе останется всего одно определение шаблона и все будут ссылаться на него. Однако все равно до линковки во всех единицах трансляции будет своя копия интовой инстанциации шаблона.
Use your weaknesses to solve your problems. Stay cool.
#compiler #cppcore #template
Когда мы говорим про шаблоны и их линковку, нам важно видеть все детали общей картины. Иначе полное понимание так и не придет. Поэтому сегодня немного больше приоткроем линковочные тайны темплейтов.
Думаю, что не будет грубым обобщением сказать, что каждый из нас пользовался шаблонным классом std::vector. И это хорошо, все имеют опыт с ним и всем будет проще понимать, о чем сейчас пойдет речь.
Представим, что мы разрабатываем какое-то приложение или отдельный сервис. Этот сервис состоит из отдельных кодовых модулей, которые отдельно компилируются и линкуются вместе для получения готового бинарника. Так вот очень легко допустить, что во многих модулях одного и того же сервиса используется вектор интов. std::vector<int>. Обычно мы просто инклюдим в эти модули хэдэр вектора и после используем его. Таким образом происходит неявная инстанциация. То есть компилятор на этапе компиляции модулей сам инстанциацирует интовую специализацию вектора в каждой единице трансляции и использует ее в коде текущего юнита.
Однако, погодите-ка. То есть у нас в нескольких единицах трансляции есть одна и та же скомпилированная сущность и при линковке это не вызывает никаких проблем. Как так?
Вот, что стандарт говорит по поводу этого:
There can be more than one definition in a program of each of the following:
class type, enumeration type, inline function, inline variable(since C++17),
templated entity(template or member of template, but not full template specialization),
as long as all of the following is true[...]
Стандарт разрешает шаблонным сущностям иметь больше, чем одно определение на всю программу. И не более одного на каждую единицу трансляции.
И кстати, утверждение, что шаблоны неявно помечены inline - неверно. Но об этом позже.
За счет чего допускается возможность наличия нескольких определений сущности в программе? Если вы читали гайд по inline(можете найти в закрепе), то, наверняка, знаете ответ. За счет слабых символов.
Скомпилируем гццшкой самую простенькую функцию:
void foo() {
std::vector<int> vec(10);
vec[0] = 1;
}
А утилитка nm покажет нам природу символов в бинаре. Там будет генерироваться оч много функций и символов связанных с вектором, поэтому разберем только один пример с конструктором, символ которого выглядит так:
0000000000000000 W std::vector<int, std::allocator<int> >::vector(unsigned long, std::allocator<int> const&)
Вот эта буковка W говорит, что этот символ - слабый. А слабые символы могут быть перезаписаны во время линковки. Линковщик просто сам выберет одно понравившееся ему определение из всех существующих в программе и перезапишет им остальные. Таким образом в программе останется всего одно определение шаблона и все будут ссылаться на него. Однако все равно до линковки во всех единицах трансляции будет своя копия интовой инстанциации шаблона.
Use your weaknesses to solve your problems. Stay cool.
#compiler #cppcore #template