Обычный пример сортировки.
В C qsort
берет указатель на функцию сравнения. Вообще говоря, будет одна копия кода qsort
, который не является встроенным. Он совершит вызов через указатель на процедуру сравнения - это, конечно же, также не указано.
В C++ std::sort
является шаблоном, и он может принимать объект-функтор в качестве компаратора. Существует другая копия std::sort
для каждого другого типа, используемого в качестве компаратора. Предполагая, что вы используете класс функтора с перегруженным operator()
, тогда вызов компаратора может легко вставить в эту копию std::sort
.
Итак, шаблоны дают вам больше инкрустировки, потому что есть больше копий кода sort
, каждый из которых может встроить другой компаратор. Вложение - довольно хорошая оптимизация, а процедуры сортировки выполняют множество сравнений, поэтому вы можете часто измерять std::sort
, работая быстрее, чем эквивалент qsort
. Стоимость этого - шанс намного большего кода - если ваша программа использует множество разных компараторов, тогда вы получаете много разных копий подпрограммы сортировки, каждая из которых использует другой компаратор, запеченный в нем.
В принципе нет причин, по которым реализация C не может встроить qsort
в то место, которое оно вызывается. Затем, если он был вызван с именем функции, оптимизатор мог теоретически заметить, что в точке, которую он использует, указатель функции должен по-прежнему указывать на эту же функцию. Затем он может встроить вызов функции, и результат будет похож на результат с std::sort
. Но на практике компиляторы, как правило, не делают первого шага, встраивая qsort
. Это потому, что (а) оно велико и (б) оно находится в другой единицы перевода, обычно скомпилированной в какую-то библиотеку, с которой связана ваша программа, и (в) сделать это таким образом, у вас будет встроенная копия qsort
для каждого обращения к нему, а не только для каждого другого компаратора. Таким образом, это было бы еще более раздутым, чем C++, если бы реализация не могла найти способ распространять код в случаях, когда qsort
вызывается в разных местах с тем же компаратором.
Таким образом, функции общего назначения, такие как qsort
в C, имеют некоторые накладные расходы в связи с вызовами через указатели функций или другие косвенные [*]. Шаблоны на C++ - это общий способ хранения исходного кода, но обеспечение его компиляции специальной функцией (или несколькими такими функциями). Надеемся, что код специального назначения будет быстрее.
Стоит отметить, что шаблоны никоим образом не связаны с производительностью. std::sort
сам по себе более общий, чем qsort
в некотором роде. Например, qsort
сортирует только массивы, тогда как std::sort
может сортировать все, что обеспечивает итератор с произвольным доступом. Он может, например, сортировать deque
, который под крышками представляет собой несколько непересекающихся массивов, выделенных отдельно. Поэтому использование шаблонов не обязательно дает какую-либо выгоду от производительности, это может быть сделано по другим причинам. Просто случается, что шаблоны влияют на производительность.
[*] еще один пример с сортировкой - qsort
принимает целочисленный параметр, указывающий, насколько велик каждый элемент массива, и когда он перемещает элементы, он поэтому должен вызывать memcpy
или аналогично значению этой переменной. std::sort
знает во время компиляции точный тип элементов и, следовательно, точный размер. Он может встроить вызов конструктора копии, который, в свою очередь, может перевести на команды для копирования этого количества байтов. Как и в случае встроенного компаратора, часто бывает возможно скопировать ровно 4 (или 8, или 16 или любые) байты быстрее, чем вы могли бы получить, вызывая процедуру, которая копирует переменное количество байтов, передавая ему значение 4 (или 8 , или 16, или что-то еще). Как и раньше, если вы вызывали qsort
с литеральным значением для размера, и этот вызов в qsort
был встроен, тогда компилятор мог выполнить ту же оптимизацию в C. Но на практике вы этого не видите.
'шаблон' есть время компиляции. Это может сократить время выполнения, но увеличит время компиляции. Если использовать без предосторожности, тогда это может привести к раздуванию кода в некоторых случаях. – iammilind