Смарт-линкер может распознать, когда две различные функции тела идентичны и объединить их в один символ. Это называется «Складывание COMDAT» в MSVC и «дублирование снятия» в большинстве других мест. Например, следующие две функции могут быть скомпилированы идентично на PPC, хотя они принимают разные типы, потому что типы имеют одинаковый размер и ведут себя одинаково в данной ситуации.
template<typename T>
GetLowBit(T foo) { return T & 1; }
GetLowBit<unsigned long>(ulong x); // compiles to "li r4, 1 ; and r3, r3, r4 ; blr "
GetLowBit<signed long>(long x); // also compiles to "li r4, 1 ; and r3, r3, r4 ; blr "
и поэтому компоновщик может сделать и их «имена» указывают на то же место, как это было, так что вызов GetLowBit<unsigned long>
идет по тому же адресу, как призыв к GetLowBit<signed long>
. Таким образом, в общем, можно скомпоновать различные шаблонные экземпляры функций, которые одинаково работают на одинаковых типоразмерах. (В частности, контейнеры, в которых хранятся указатели или перечисления, как правило, складываются вместе.)
Это не просто происходит для функций шаблона. Некоторые линкеры могут заметить, что любые две функции имеют одинаковые тела и объединяют их. В частности, я вижу, что MSVC имеет тенденцию к коллапсу каждой виртуальной функции, которая ничего не делает, но вернуться в один убер-функции, например, для
class A
{
virtual void nothing() {};
}
class B
{
virtual void empty() {};
}
Я часто вижу (в дизассемблер при отладке что-то еще), что виртуальные таблицы записи для nothing
и empty
оба указывают на ту же функциональную часть, которая, в свою очередь, является только ret
.
Вы можете рассчитывать на это? Нет. Это особенность, которую смарт-линкер может предусмотреть функции , когда он замечает их. Ваш компоновщик может быть глупым —, там много паршивых компиляторов. Это может произойти только в том случае, если вы предоставите определенные аргументы команды. Это может случиться для некоторых функций, но не для других по причинам, известным только для компоновщика. Он может даже отличаться от сборки для сборки в зависимости от того, что еще находится в программе.
В общем, да, это может быть, и когда это произойдет, это может быть хорошей экономией в исполняемом размере. Но вы не можете рассчитывать на это, если вы не знакомы с вашим компоновщиком и всеми его функциями.
В приведенном ниже примере, вероятно, невозможно свернуть разные экземпляры вместе, поскольку размер параметра элементов будет другим. Это, в свою очередь, повлияет на функцию prolog/epilog (где стек распределяется, а затем очищается). – Crashworks
@Crashworks_So распределение стека не может выполнять задание резервирования различных размеров кадра стека за каждый вызов функции? – Pooria
Вы должны попробовать посмотреть несколько вызовов функций в окне разборки отладчика и посмотреть, как фактически распределяется стек. Это, вероятно, было бы более просвещенным, чем все, что я мог вписаться в эту маленькую коробку. =) – Crashworks